Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (11.3 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 System.IO;
45
using Pithos.Interfaces;
46
using System;
47
using System.Collections.Generic;
48
using System.Linq;
49
using Pithos.Network;
50

    
51
namespace Pithos.Core.Agents
52
{
53

    
54
    /// <summary>
55
    /// Extension methods for collections
56
    /// </summary>
57
    public static class CollectionExtensions
58
    {
59
        public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> generate)
60
        {
61
            foreach (var item in list)
62
            {
63
                if (match(item))
64
                    foreach (var newItem in generate(item))
65
                    {
66
                        yield return newItem;
67
                    }
68
                else
69
                    yield return item;
70
            }
71
        }
72

    
73

    
74
        /// <summary>
75
        /// Remove the first message in a queue that matches the predicate
76
        /// </summary>
77
        /// <param name="queue">The queue</param>
78
        /// <param name="predicate">The condition to match</param>
79
        /// <remarks>Removes the first message that matches the predicate by dequeing all 
80
        /// messages and re-enqueing all except the first matching message</remarks>
81
        public static void RemoveFirst<TMessage>(this ConcurrentQueue<TMessage> queue, Func<TMessage, bool> predicate)
82
        {
83
            //Can this work? Dequeue all items 
84
            //and then enqueue everything except the filtered items
85

    
86
            //Possible problems: 
87
            //* A matching item may be dequeued between one TryDequeue and the next
88
            var temp = new Queue<TMessage>();
89
            TMessage message;
90
            var alreadyFound = false;
91
            while (queue.TryDequeue(out message))
92
            {
93
                if (!predicate(message) || alreadyFound)
94
                    temp.Enqueue(message);
95
                else
96
                {
97
                    alreadyFound = true;
98
                }
99
            }
100

    
101
            queue.AddFromEnumerable(temp);
102
        }
103

    
104

    
105
        /// <summary>
106
        /// Return only the info objects that are below one of the filter Uris
107
        /// </summary>
108
        /// <param name="infos">ObjectInfo items to filter</param>
109
        /// <param name="filterUris">List of filter Uris</param>
110
        /// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns>
111
        public static IEnumerable<ObjectInfo> FilterBelow(this IEnumerable<ObjectInfo> infos,List<Uri> filterUris  )
112
        {
113
            if (filterUris == null)
114
                return infos;
115
            if (filterUris.Count == 0)
116
                return infos;
117
            //Allow all objects whose Uris start with any of the filters            
118
            var filteredUris = from info in infos
119
                                  where filterUris.Any(f => info.Uri.IsAtOrBelow(f)) 
120
                                  select info;            
121
            return filteredUris;
122
        }
123

    
124
        /// <summary>
125
        /// Return only the info objects that are directly below one of the filter Uris
126
        /// </summary>
127
        /// <param name="infos">ObjectInfo items to filter</param>
128
        /// <param name="filterUris">List of filter Uris</param>
129
        /// <returns>An enumerable of ObjectInfo items. If the list of filter Uris is empty or null, the original list is returned</returns>
130
        public static IEnumerable<ObjectInfo> FilterDirectlyBelow(this IEnumerable<ObjectInfo> infos, List<Uri> filterUris)
131
        {
132
            if (filterUris == null)
133
                return infos;
134
            if (filterUris.Count == 0)
135
                return infos;
136
            //Allow all objects whose Uris start with any of the filters            
137
            var filteredUris = from info in infos
138
                                  where filterUris.Any(f => info.Uri.IsAtOrDirectlyBelow(f)) 
139
                                  select info;            
140
            return filteredUris;
141
        }
142

    
143
        /// <summary>
144
        /// Checkes whether a Uri is below a root Uri
145
        /// </summary>
146
        /// <param name="target"></param>
147
        /// <param name="root"></param>
148
        /// <returns></returns>
149
        public static bool IsAtOrBelow(this Uri target,Uri root)
150
        {
151
            if(root == null)
152
                throw new ArgumentNullException("root");
153
            if(target == null)
154
                throw new ArgumentNullException("target");
155
            Contract.EndContractBlock();
156

    
157
            var targetSegments = target.Segments;
158
            var rootSegments = root.Segments;
159

    
160
            return InnerAtOrBelow(targetSegments, rootSegments);
161
        }
162

    
163
        private static bool InnerAtOrBelow(string[] targetSegments, string[] rootSegments)
164
        {
165
            //If the uri is shorter than the root, no point in comparing
166
            if (targetSegments.Length < rootSegments.Length)
167
                return false;
168
            //If the uri is below the root, it should match the root's segments one by one
169
            //If there is any root segment that doesn't match its corresponding target segment,
170
            //the target is not below the root
171
            //DON'T FORGET that Uri segments include the slashes. Must remove them to ensure proper checks
172
            var mismatch = rootSegments
173
                .Where((t, i) => !String.Equals(targetSegments[i].TrimEnd('/'), t.TrimEnd('/'),StringComparison.InvariantCultureIgnoreCase))
174
                .Any();
175
            return !mismatch;
176
        }
177

    
178

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

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

    
203
        public static bool IsSharedTo(this string targetPath,AccountInfo account)
204
        {
205
            var othersFolder = Path.Combine(account.AccountPath, FolderConstants.OthersFolder);
206
            var isShared = targetPath.IsAtOrBelow(othersFolder);
207
            return isShared;
208

    
209
        }
210

    
211
        /// <summary>
212
        /// Checkes whether a file path is below a root path
213
        /// </summary>
214
        /// <param name="targetPath"></param>
215
        /// <param name="rootPath"></param>
216
        /// <returns></returns>
217
        public static bool IsAtOrBelow(this string targetPath, string rootPath)
218
        {
219
            if(String.IsNullOrWhiteSpace(targetPath))
220
                throw new ArgumentNullException("targetPath");
221
            if(String.IsNullOrWhiteSpace(rootPath))
222
                throw new ArgumentNullException("rootPath");
223
            Contract.EndContractBlock();
224

    
225
            var targetSegments = targetPath.Split('\\');
226
            var rootSegments = rootPath.Split('\\');
227

    
228
            return InnerAtOrBelow(targetSegments, rootSegments);
229
        }
230

    
231
        /// <summary>
232
        /// Checks whether a file path is directly below a root path
233
        /// </summary>
234
        /// <param name="targetPath"></param>
235
        /// <param name="rootPath"></param>
236
        /// <returns></returns>
237
        public static bool IsAtOrDirectlyBelow(this string targetPath, string rootPath)
238
        {
239
            if (String.IsNullOrWhiteSpace(targetPath))
240
                throw new ArgumentNullException("targetPath");
241
            if (String.IsNullOrWhiteSpace(rootPath))
242
                throw new ArgumentNullException("rootPath");
243
            Contract.EndContractBlock();
244

    
245
            if (targetPath==rootPath)
246
                return true;
247
            var targetSegments = targetPath.Split('\\');
248
            var rootSegments = rootPath.Split('\\');
249

    
250
            return
251
                //If the target is directly below the root, it will have exactly 
252
                //one segment more than the root            
253
                targetSegments.Length == rootSegments.Length + 1
254
                //Ensure that the candidate target is indeed below the root
255
                && InnerAtOrBelow(targetSegments, rootSegments);
256
        }
257

    
258
        /// <summary>
259
        /// Returns the part of the target string that comes after the prefix string.
260
        /// The target is not modified if it doesn't start with the prefix string
261
        /// </summary>
262
        /// <param name="target"></param>
263
        /// <param name="prefix"></param>
264
        /// <returns></returns>
265
        public static string After(this string target,string prefix)
266
        {            
267
            //If the prefix or the target are empty, return the target
268
            if (String.IsNullOrWhiteSpace(prefix) || String.IsNullOrWhiteSpace(target))
269
                return target;
270
            if (target.StartsWith(prefix))
271
                return target.Remove(0,prefix.Length);
272
            return target;
273
        }
274
    }
275

    
276

    
277
}