Fix for duplicates in selectivesync display code
[pithos-ms-client] / trunk / Pithos.Client.WPF / Utils / EnumerableExtensions.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Linq;
5 using System.Linq.Expressions;
6 using System.Text;
7 using System.Text.RegularExpressions;
8 using Pithos.Client.WPF.SelectiveSynch;
9 using Pithos.Core;
10 using Pithos.Interfaces;
11
12 namespace Pithos.Client.WPF.Utils
13 {
14     public static class EnumerableExtensions
15     {
16         public static IEnumerable<T> Slice<T>(this IEnumerable<T> collection, int start, int end)
17         {
18             int index = 0;
19             int count = 0;
20
21             if (collection == null)
22                 throw new ArgumentNullException("collection");
23
24             // Optimise item count for ICollection interfaces.
25             if (collection is ICollection<T>)
26                 count = ((ICollection<T>)collection).Count;
27             else if (collection is ICollection)
28                 count = ((ICollection)collection).Count;
29             else
30             {
31                 count = collection.Count();
32             }
33
34             // Get start/end indexes, negative numbers start at the end of the collection.
35             if (start < 0)
36                 start += count;
37
38             if (end < 0)
39                 end += count;
40
41             foreach (var item in collection)
42             {
43                 if (index >= end)
44                     yield break;
45
46                 if (index >= start)
47                     yield return item;
48
49                 ++index;
50             }
51         }
52
53         public static IEnumerable<Node<T>> ToTree<TSource,T>(this IEnumerable<TSource> enumerable,Func<TSource,string> pathFunc,Func<TSource,T> valueFunc,string delimiter="/")
54         {
55             var orderedItems=enumerable.OrderBy(pathFunc);
56             var lookups = new Dictionary<string,Node<T>>();
57             var nodes = new List<Node<T>>();
58             foreach (var item in orderedItems)
59             {
60                 var path = pathFunc(item);
61                 var value = valueFunc(item);
62                 var newNode = new Node<T> { Path = path,Data=value };
63                 lookups[path] = newNode;
64
65                 var lastIndex = path.LastIndexOf(delimiter, StringComparison.Ordinal);
66                 var upTo = lastIndex < 0 ? path.Length - 1 : lastIndex;
67                 var parentPath = path.Substring(0, upTo);              
68   
69                 Node<T> parent;
70                 if (lookups.TryGetValue(parentPath, out parent))
71                 {
72                     parent.Children.Add(newNode);   
73                     parent.Children.Sort((x,y)=>String.CompareOrdinal(x.Path, y.Path));
74                 }
75                 else
76                     nodes.Add(newNode);
77
78             }
79             return nodes;
80         }
81
82         public static List<DirectoryRecord> ToTree(this IEnumerable<ObjectInfo> enumerable)
83         {
84             //Order the items to ensure that children always come after their parents
85             var orderedItems=enumerable.OrderBy(o=>o.Uri.ToString());
86             //Each item is stored in lookups
87             var lookups = new Dictionary<string,DirectoryRecord>();
88             
89             //RootNodes contains only the root nodes
90             var rootNodes = new List<DirectoryRecord>();            
91
92             foreach (var item in orderedItems)
93             {
94                 var path = item.Uri.ToString();
95                 //Calculate the parent path
96                 var parentPath = GetParentPath(path);
97                 var parentName = GetParentPath(item.Name);
98                 DirectoryRecord parent;                
99
100
101                 //First ensure that the parent items exist
102
103                 var parts = item.Name.Split('/');
104
105                 //Dont't add files
106                 if (!item.IsDirectory)
107                 {
108                     //But check to ensure that we DO have it's parent on record
109                     //It it exist
110                     if (lookups.TryGetValue(parentPath, out parent))
111                     {
112                         //Just continue
113                         continue;
114                     }
115                     //If the item is directly below its parent container, there is no path to add
116                     if (String.IsNullOrWhiteSpace(parentName))
117                         continue;
118                     //Otherwise we need to add it, because it is missing from the list
119                     //Store each item using its current path
120                     //Since this is not a directory, we won't add the item itself
121                     AddParentNodes(lookups, rootNodes, parts, item);
122                 }
123                 else
124                 {
125                     AddParentNodes(lookups, rootNodes, parts, item);
126
127                     //Store each item using its current path
128                     var newNode = new DirectoryRecord {DisplayName = parts.Last(), ObjectInfo = item};
129                     AddNode(rootNodes, lookups, newNode);
130                 }
131                 
132             }
133             return rootNodes;
134         }
135
136         private static void AddParentNodes(Dictionary<string, DirectoryRecord> lookups, List<DirectoryRecord> rootNodes, string[] parts, ObjectInfo item)
137         {
138             for (int i = 0; i < parts.Length - 1; i++)
139             {
140                 var nodeName = String.Join("/", parts, 0, i + 1);
141                 var storageUri = GetStorageUri(item);
142                 var nodeKey = String.Format("{0}/{1}/{2}/{3}", storageUri, item.Account,item.Container, nodeName);
143                 //If the node was already addeds, skip
144                 if (lookups.ContainsKey(nodeKey))
145                     continue;
146                 var newNode = new DirectoryRecord
147                                   {
148                                       DisplayName = parts[i],
149                                       ObjectInfo = new ObjectInfo
150                                                        {
151                                                            Account = item.Account,
152                                                            Container = item.Container,
153                                                            Name = nodeName,
154                                                            StorageUri=item.StorageUri,
155                                                            Content_Type = "application/directory"
156                                                        }
157                                   };
158                 AddNode(rootNodes, lookups, newNode);
159             }
160         }
161
162         private static string GetStorageUri(ObjectInfo item)
163         {
164             var storageParts = item.StorageUri.ToString().Split('/');
165             var accountUri = String.Join("/", storageParts, 0, storageParts.Length - 1);
166             return accountUri;
167         }
168
169         private static void AddNode(List<DirectoryRecord> rootNodes, Dictionary<string, DirectoryRecord> lookups, DirectoryRecord newNode)
170         {
171             DirectoryRecord parent;
172             var path = newNode.ObjectInfo.Uri.ToString();
173             var parentPath = GetParentPath(path);
174             lookups[path] = newNode;
175
176             //If the record is a directory, we need to add it no matter what
177
178             //If it is a file, we need to check that its parent node exists
179             //If the parent node doesn't exist, we need to add a node for it.
180             //We need to add nodes for all parents recursively,in case they don't exist
181
182             //Does a parent item exist? 
183             if (lookups.TryGetValue(parentPath, out parent))
184             {
185                 //If so, add the current item under its parent
186                 parent.Directories.Add(newNode);
187                 parent.Directories.Sort((x, y) => String.CompareOrdinal(x.Uri.ToString(), y.Uri.ToString()));
188             }
189             else
190                 //Otherwise add it to the list of root nodes
191                 rootNodes.Add(newNode);
192         }
193
194         private static string GetParentPath(string path)
195         {            
196             var lastIndex = path.LastIndexOf("/", StringComparison.Ordinal);
197             if (lastIndex < 0)
198                 return null;
199             var parentPath = path.Substring(0, lastIndex);
200             return parentPath;
201         }
202
203         static readonly Regex PascalCaseRegex = new Regex("[a-z][A-Z]", RegexOptions.Compiled);        
204         public static string Name(this  Enum value)
205         {
206             var name = Enum.GetName(value.GetType(), value);            
207             return PascalCaseRegex.Replace(name, m => m.Value[0] + " " + char.ToLower(m.Value[1]));            
208         }
209     }
210 }