From 18f04af051ff252137500b58d05ef861d30b558b Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 17 Apr 2019 08:46:51 -0400 Subject: [PATCH] tree node traversal for folder --- src/Core/Models/Domain/ITreeNodeObject.cs | 8 +++ src/Core/Models/Domain/TreeNode.cs | 18 +++++++ src/Core/Models/View/CollectionView.cs | 2 +- src/Core/Models/View/FolderView.cs | 2 +- src/Core/Services/FolderService.cs | 30 ++++++++--- src/Core/Utilities/CoreHelpers.cs | 65 ++++++++++++++++++++++- 6 files changed, 115 insertions(+), 10 deletions(-) create mode 100644 src/Core/Models/Domain/ITreeNodeObject.cs create mode 100644 src/Core/Models/Domain/TreeNode.cs diff --git a/src/Core/Models/Domain/ITreeNodeObject.cs b/src/Core/Models/Domain/ITreeNodeObject.cs new file mode 100644 index 000000000..f91c89151 --- /dev/null +++ b/src/Core/Models/Domain/ITreeNodeObject.cs @@ -0,0 +1,8 @@ +namespace Bit.Core.Models.Domain +{ + public interface ITreeNodeObject + { + string Id { get; set; } + string Name { get; set; } + } +} diff --git a/src/Core/Models/Domain/TreeNode.cs b/src/Core/Models/Domain/TreeNode.cs new file mode 100644 index 000000000..cd0be31d5 --- /dev/null +++ b/src/Core/Models/Domain/TreeNode.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; + +namespace Bit.Core.Models.Domain +{ + public class TreeNode where T : ITreeNodeObject + { + public T Parent { get; set; } + public T Node { get; set; } + public List> Children { get; set; } + + public TreeNode(T node, string name, T parent) + { + Parent = parent; + Node = node; + Node.Name = name; + } + } +} diff --git a/src/Core/Models/View/CollectionView.cs b/src/Core/Models/View/CollectionView.cs index 4f90c20b8..23251a642 100644 --- a/src/Core/Models/View/CollectionView.cs +++ b/src/Core/Models/View/CollectionView.cs @@ -2,7 +2,7 @@ namespace Bit.Core.Models.View { - public class CollectionView : View + public class CollectionView : View, ITreeNodeObject { public CollectionView() { } diff --git a/src/Core/Models/View/FolderView.cs b/src/Core/Models/View/FolderView.cs index c6adf5756..e09eea1d3 100644 --- a/src/Core/Models/View/FolderView.cs +++ b/src/Core/Models/View/FolderView.cs @@ -3,7 +3,7 @@ using System; namespace Bit.Core.Models.View { - public class FolderView : View + public class FolderView : View, ITreeNodeObject { public FolderView() { } diff --git a/src/Core/Services/FolderService.cs b/src/Core/Services/FolderService.cs index 99c1efb0b..154a5ccca 100644 --- a/src/Core/Services/FolderService.cs +++ b/src/Core/Services/FolderService.cs @@ -1,6 +1,4 @@ using Bit.Core.Abstractions; -using Bit.Core.Enums; -using Bit.Core.Exceptions; using Bit.Core.Models.Data; using Bit.Core.Models.Domain; using Bit.Core.Models.Request; @@ -9,10 +7,7 @@ using Bit.Core.Models.View; using Bit.Core.Utilities; using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Net.Http; -using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Bit.Core.Services @@ -21,7 +16,7 @@ namespace Bit.Core.Services { private const string Keys_CiphersFormat = "ciphers_{0}"; private const string Keys_FoldersFormat = "folders_{0}"; - private const string NestingDelimiter = "/"; + private const char NestingDelimiter = '/'; private List _decryptedFolderCache; private readonly ICryptoService _cryptoService; @@ -115,7 +110,28 @@ namespace Bit.Core.Services return _decryptedFolderCache; } - // TODO: nested stuff + public async Task>> GetAllNestedAsync() + { + var folders = await GetAllDecryptedAsync(); + var nodes = new List>(); + foreach(var f in folders) + { + var folderCopy = new FolderView + { + Id = f.Id, + RevisionDate = f.RevisionDate + }; + CoreHelpers.NestedTraverse(nodes, 0, f.Name.Split(NestingDelimiter), folderCopy, null, + NestingDelimiter); + } + return nodes; + } + + public async Task> GetNestedAsync(string id) + { + var folders = await GetAllNestedAsync(); + return CoreHelpers.GetTreeNodeObject(folders, id); + } public async Task SaveWithServerAsync(Folder folder) { diff --git a/src/Core/Utilities/CoreHelpers.cs b/src/Core/Utilities/CoreHelpers.cs index 564c0d2e0..e09051f81 100644 --- a/src/Core/Utilities/CoreHelpers.cs +++ b/src/Core/Utilities/CoreHelpers.cs @@ -1,4 +1,7 @@ -using System; +using Bit.Core.Models.Domain; +using System; +using System.Collections.Generic; +using System.Linq; using System.Text.RegularExpressions; namespace Bit.Core.Utilities @@ -87,5 +90,65 @@ namespace Bit.Core.Utilities } return null; } + + public static void NestedTraverse(List> nodeTree, int partIndex, string[] parts, + T obj, T parent, char delimiter) where T : ITreeNodeObject + { + if(parts.Length <= partIndex) + { + return; + } + + var end = partIndex == parts.Length - 1; + var partName = parts[partIndex]; + foreach(var n in nodeTree) + { + if(n.Node.Name != parts[partIndex]) + { + continue; + } + if(end && n.Node.Id != obj.Id) + { + // Another node with the same name. + nodeTree.Add(new TreeNode(obj, partName, parent)); + return; + } + NestedTraverse(n.Children, partIndex + 1, parts, obj, n.Node, delimiter); + return; + } + if(!nodeTree.Any(n => n.Node.Name == partName)) + { + if(end) + { + nodeTree.Add(new TreeNode(obj, partName, parent)); + return; + } + var newPartName = string.Concat(parts[partIndex], delimiter, parts[partIndex + 1]); + var newParts = new List { newPartName }; + var newPartsStartFrom = partIndex + 2; + newParts.AddRange(new ArraySegment(parts, newPartsStartFrom, parts.Length - newPartsStartFrom)); + NestedTraverse(nodeTree, 0, newParts.ToArray(), obj, parent, delimiter); + } + } + + public static TreeNode GetTreeNodeObject(List> nodeTree, string id) where T : ITreeNodeObject + { + foreach(var n in nodeTree) + { + if(n.Node.Id == id) + { + return n; + } + else if(n.Children != null) + { + var node = GetTreeNodeObject(n.Children, id); + if(node != null) + { + return node; + } + } + } + return null; + } } }