Fix for duplicates in selectivesync display code
[pithos-ms-client] / trunk / Pithos.Client.WPF / Utils / EnumerableExtensions.cs
index d76fc09..7c3f15c 100644 (file)
@@ -4,7 +4,9 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Text;
+using System.Text.RegularExpressions;
 using Pithos.Client.WPF.SelectiveSynch;
+using Pithos.Core;
 using Pithos.Interfaces;
 
 namespace Pithos.Client.WPF.Utils
@@ -79,30 +81,130 @@ namespace Pithos.Client.WPF.Utils
 
         public static List<DirectoryRecord> ToTree(this IEnumerable<ObjectInfo> enumerable)
         {
+            //Order the items to ensure that children always come after their parents
             var orderedItems=enumerable.OrderBy(o=>o.Uri.ToString());
+            //Each item is stored in lookups
             var lookups = new Dictionary<string,DirectoryRecord>();
-            var nodes = new List<DirectoryRecord>();
+            
+            //RootNodes contains only the root nodes
+            var rootNodes = new List<DirectoryRecord>();            
+
             foreach (var item in orderedItems)
             {
                 var path = item.Uri.ToString();
-                var newNode = new DirectoryRecord{ DisplayName=item.Name.Split('/').Last(),ObjectInfo=item};
-                lookups[path] = newNode;
+                //Calculate the parent path
+                var parentPath = GetParentPath(path);
+                var parentName = GetParentPath(item.Name);
+                DirectoryRecord parent;                
 
-                var lastIndex = path.LastIndexOf("/", StringComparison.Ordinal);
-                var upTo = lastIndex < 0 ? path.Length - 1 : lastIndex;
-                var parentPath = path.Substring(0, upTo);              
-  
-                DirectoryRecord parent;
-                if (lookups.TryGetValue(parentPath, out parent))
+
+                //First ensure that the parent items exist
+
+                var parts = item.Name.Split('/');
+
+                //Dont't add files
+                if (!item.IsDirectory)
                 {
-                    parent.Directories.Add(newNode);   
-                    parent.Directories.Sort((x,y)=>String.CompareOrdinal(x.Uri.ToString(), y.Uri.ToString()));
+                    //But check to ensure that we DO have it's parent on record
+                    //It it exist
+                    if (lookups.TryGetValue(parentPath, out parent))
+                    {
+                        //Just continue
+                        continue;
+                    }
+                    //If the item is directly below its parent container, there is no path to add
+                    if (String.IsNullOrWhiteSpace(parentName))
+                        continue;
+                    //Otherwise we need to add it, because it is missing from the list
+                    //Store each item using its current path
+                    //Since this is not a directory, we won't add the item itself
+                    AddParentNodes(lookups, rootNodes, parts, item);
                 }
                 else
-                    nodes.Add(newNode);
+                {
+                    AddParentNodes(lookups, rootNodes, parts, item);
 
+                    //Store each item using its current path
+                    var newNode = new DirectoryRecord {DisplayName = parts.Last(), ObjectInfo = item};
+                    AddNode(rootNodes, lookups, newNode);
+                }
+                
             }
-            return nodes;
+            return rootNodes;
+        }
+
+        private static void AddParentNodes(Dictionary<string, DirectoryRecord> lookups, List<DirectoryRecord> rootNodes, string[] parts, ObjectInfo item)
+        {
+            for (int i = 0; i < parts.Length - 1; i++)
+            {
+                var nodeName = String.Join("/", parts, 0, i + 1);
+                var storageUri = GetStorageUri(item);
+                var nodeKey = String.Format("{0}/{1}/{2}/{3}", storageUri, item.Account,item.Container, nodeName);
+                //If the node was already addeds, skip
+                if (lookups.ContainsKey(nodeKey))
+                    continue;
+                var newNode = new DirectoryRecord
+                                  {
+                                      DisplayName = parts[i],
+                                      ObjectInfo = new ObjectInfo
+                                                       {
+                                                           Account = item.Account,
+                                                           Container = item.Container,
+                                                           Name = nodeName,
+                                                           StorageUri=item.StorageUri,
+                                                           Content_Type = "application/directory"
+                                                       }
+                                  };
+                AddNode(rootNodes, lookups, newNode);
+            }
+        }
+
+        private static string GetStorageUri(ObjectInfo item)
+        {
+            var storageParts = item.StorageUri.ToString().Split('/');
+            var accountUri = String.Join("/", storageParts, 0, storageParts.Length - 1);
+            return accountUri;
+        }
+
+        private static void AddNode(List<DirectoryRecord> rootNodes, Dictionary<string, DirectoryRecord> lookups, DirectoryRecord newNode)
+        {
+            DirectoryRecord parent;
+            var path = newNode.ObjectInfo.Uri.ToString();
+            var parentPath = GetParentPath(path);
+            lookups[path] = newNode;
+
+            //If the record is a directory, we need to add it no matter what
+
+            //If it is a file, we need to check that its parent node exists
+            //If the parent node doesn't exist, we need to add a node for it.
+            //We need to add nodes for all parents recursively,in case they don't exist
+
+            //Does a parent item exist? 
+            if (lookups.TryGetValue(parentPath, out parent))
+            {
+                //If so, add the current item under its parent
+                parent.Directories.Add(newNode);
+                parent.Directories.Sort((x, y) => String.CompareOrdinal(x.Uri.ToString(), y.Uri.ToString()));
+            }
+            else
+                //Otherwise add it to the list of root nodes
+                rootNodes.Add(newNode);
+        }
+
+        private static string GetParentPath(string path)
+        {            
+            var lastIndex = path.LastIndexOf("/", StringComparison.Ordinal);
+            if (lastIndex < 0)
+                return null;
+            var parentPath = path.Substring(0, lastIndex);
+            return parentPath;
+        }
+
+        static readonly Regex PascalCaseRegex = new Regex("[a-z][A-Z]", RegexOptions.Compiled);        
+        public static string Name(this  Enum value)
+        {
+            var name = Enum.GetName(value.GetType(), value);            
+            return PascalCaseRegex.Replace(name, m => m.Value[0] + " " + char.ToLower(m.Value[1]));            
         }
     }
 }