Statistics
| Branch: | Revision:

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

History | View | Annotate | Download (11.4 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

    
60
        
61

    
62

    
63
        public static IEnumerable<T> Replace<T>(this IEnumerable<T> list,Func<T,bool> match, Func<T,IEnumerable<T>> generate)
64
        {
65
            foreach (var item in list)
66
            {
67
                if (match(item))
68
                    foreach (var newItem in generate(item))
69
                    {
70
                        yield return newItem;
71
                    }
72
                else
73
                    yield return item;
74
            }
75
        }
76

    
77

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

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

    
105
            queue.AddFromEnumerable(temp);
106
        }
107

    
108

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

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

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

    
161
            var targetSegments = target.Segments;
162
            var rootSegments = root.Segments;
163

    
164
            return InnerAtOrBelow(targetSegments, rootSegments);
165
        }
166

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

    
182

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

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

    
207
        public static bool IsSharedTo(this string targetPath,AccountInfo account)
208
        {
209
            var othersFolder = Path.Combine(account.AccountPath, FolderConstants.OthersFolder);
210
            var isShared = targetPath.IsAtOrBelow(othersFolder);
211
            return isShared;
212

    
213
        }
214

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

    
229
            var targetSegments = targetPath.Split('\\');
230
            var rootSegments = rootPath.Split('\\');
231

    
232
            return InnerAtOrBelow(targetSegments, rootSegments);
233
        }
234

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

    
249
            if (targetPath==rootPath)
250
                return true;
251
            var targetSegments = targetPath.Split('\\');
252
            var rootSegments = rootPath.Split('\\');
253

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

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

    
280

    
281
}