TF-37 Generate tree with Map

This commit is contained in:
Dat PHAM HOANG
2021-08-31 18:08:58 +07:00
committed by Dat H. Pham
parent 41b4e8296c
commit e63298bc29
6 changed files with 44 additions and 242 deletions
@@ -1,4 +1,3 @@
import 'package:built_collection/built_collection.dart';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/core/id.dart'; import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
@@ -8,39 +7,19 @@ class MailboxNode with EquatableMixin{
static PresentationMailbox _root = PresentationMailbox(MailboxId(Id('root'))); static PresentationMailbox _root = PresentationMailbox(MailboxId(Id('root')));
PresentationMailbox item; PresentationMailbox item;
late BuiltList<MailboxNode> childrenItems; List<MailboxNode>? childrenItems;
factory MailboxNode.root() => MailboxNode(_root); factory MailboxNode.root() => MailboxNode(_root);
bool hasChildren() => childrenItems.isNotEmpty; bool hasChildren() => childrenItems?.isNotEmpty ?? false;
MailboxNode(this.item, {List<MailboxNode> child = const []}) { MailboxNode(this.item, {this.childrenItems});
this.childrenItems = BuiltList(child);
}
MailboxNode? addChild(PresentationMailbox mailbox) { void addChildNode(MailboxNode node) {
if (_validateChild(mailbox)) { if (childrenItems == null) {
final node = MailboxNode(mailbox); childrenItems = [];
_addChildNode(node);
return node;
} }
return null; childrenItems?.add(node);
}
void _addChildNode(MailboxNode node) {
childrenItems = (ListBuilder<MailboxNode>(childrenItems)
..add(node))
.build();
}
bool _validateChild(PresentationMailbox mailbox) {
if (mailbox.parentId == item.id) {
return true;
}
if (item == _root) {
return true;
}
return false;
} }
@override @override
@@ -1,8 +1,5 @@
import 'dart:collection';
import 'package:equatable/equatable.dart'; import 'package:equatable/equatable.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'mailbox_node.dart'; import 'mailbox_node.dart';
@@ -10,34 +7,6 @@ class MailboxTree with EquatableMixin {
MailboxNode root; MailboxNode root;
MailboxTree(this.root); MailboxTree(this.root);
MailboxNode? findNode(MailboxId? mailboxId) {
var result;
final queue = ListQueue<MailboxNode>();
queue.addLast(root);
while (queue.isNotEmpty && mailboxId != null) {
final currentNode = queue.removeFirst();
if (mailboxId == currentNode.item.id) {
result = currentNode;
break;
}
currentNode.childrenItems.forEach((child) {
queue.addLast(child);
});
}
return result;
}
MailboxNode? insertNode(PresentationMailbox mailbox) {
if (mailbox.parentId == null) {
return root.addChild(mailbox);
}
final foundParent = findNode(mailbox.parentId);
return foundParent != null
? foundParent.addChild(mailbox)
: root.addChild(mailbox);
}
@override @override
List<Object?> get props => [root]; List<Object?> get props => [root];
} }
@@ -1,6 +1,5 @@
import 'dart:collection'; import 'dart:collection';
import 'package:built_collection/built_collection.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart'; import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/presentation_mailbox.dart'; import 'package:model/mailbox/presentation_mailbox.dart';
@@ -9,50 +8,24 @@ import 'mailbox_tree.dart';
class TreeBuilder { class TreeBuilder {
Future<MailboxTree> generateMailboxTree(List<PresentationMailbox> mailboxesList) async { Future<MailboxTree> generateMailboxTree(List<PresentationMailbox> mailboxesList) async {
final Map<MailboxId, BuiltList<PresentationMailbox>> mapNotFoundParentMailbox = HashMap(); final Map<MailboxId, MailboxNode> mailboxDictionary = HashMap();
final tree = MailboxTree(MailboxNode.root()); final tree = MailboxTree(MailboxNode.root());
mailboxesList.forEach((mailbox) { mailboxesList.forEach((mailbox) {
if (mailbox.hasParentId()) { mailboxDictionary[mailbox.id] = MailboxNode(mailbox);
final parentId = mailbox.parentId!;
final foundParent = tree.findNode(parentId);
if (foundParent != null) {
_addDescendant(tree, mailbox, maybeDescendant: mapNotFoundParentMailbox);
} else {
final siblingTrees = mapNotFoundParentMailbox[mailbox.parentId];
if (siblingTrees != null) {
mapNotFoundParentMailbox.addAll({
parentId: (ListBuilder<PresentationMailbox>(siblingTrees)..add(mailbox)).build()
});
} else {
mapNotFoundParentMailbox.addAll({parentId: (ListBuilder<PresentationMailbox>()..add(mailbox)).build()});
}
}
} else {
_addDescendant(tree, mailbox, maybeDescendant: mapNotFoundParentMailbox);
}
}); });
mapNotFoundParentMailbox.forEach((_, listMailbox) { mailboxesList.forEach((mailbox) {
listMailbox.forEach((mailbox) { final parentId = mailbox.parentId;
_addDescendant(tree, mailbox); final parentNode = mailboxDictionary[parentId];
}); final node = mailboxDictionary[mailbox.id];
if (node != null) {
if (parentNode != null) {
parentNode.addChildNode(node);
} else {
tree.root.addChildNode(node);
}
}
}); });
return tree; return tree;
} }
MailboxNode? _addDescendant(
MailboxTree tree,
PresentationMailbox mailboxChild,
{Map<MailboxId, BuiltList<PresentationMailbox>>? maybeDescendant}
) {
final queue = ListQueue<PresentationMailbox>();
queue.addLast(mailboxChild);
while (queue.isNotEmpty) {
final current = queue.removeFirst();
tree.insertNode(current);
maybeDescendant?.remove(current.id)?.forEach((child) {
queue.addLast(child);
});
}
}
} }
@@ -11,38 +11,20 @@ void main() {
final parentMailbox = PresentationMailbox(MailboxId(Id('1'))); final parentMailbox = PresentationMailbox(MailboxId(Id('1')));
final node = MailboxNode(parentMailbox); final node = MailboxNode(parentMailbox);
final childMailbox = PresentationMailbox(MailboxId(Id('11')), parentId: MailboxId(Id('1'))); final childMailbox = PresentationMailbox(MailboxId(Id('11')), parentId: MailboxId(Id('1')));
final childNode = node.addChild(childMailbox); node.addChildNode(MailboxNode(childMailbox));
expect(node.childrenItems.length, equals(1)); expect(node.childrenItems?.length, equals(1));
expect(node.childrenItems[0], equals(childNode));
}); });
test('addChild should add new mailbox to a node', () async { test('addChildNode should add other new mailbox to a node', () async {
final parentMailbox = PresentationMailbox(MailboxId(Id('1'))); final parentMailbox = PresentationMailbox(MailboxId(Id('1')));
final node = MailboxNode(parentMailbox); final node = MailboxNode(parentMailbox);
final childMailbox = PresentationMailbox(MailboxId(Id('11')), parentId: MailboxId(Id('1'))); final childMailbox = PresentationMailbox(MailboxId(Id('11')), parentId: MailboxId(Id('1')));
final childNode = node.addChild(childMailbox); node.addChildNode(MailboxNode(childMailbox));
final childMailbox2 = PresentationMailbox(MailboxId(Id('12')), parentId: MailboxId(Id('1'))); final childMailbox2 = PresentationMailbox(MailboxId(Id('12')), parentId: MailboxId(Id('1')));
final childNode2 = node.addChild(childMailbox2); node.addChildNode(MailboxNode(childMailbox2));
expect(node.childrenItems.length, equals(2)); expect(node.childrenItems!.length, equals(2));
expect(node.childrenItems, containsAll({childNode, childNode2}));
});
test('addChild add new mailbox without parentId to a node should return null', () async {
final parentMailbox = PresentationMailbox(MailboxId(Id('1')));
final node = MailboxNode(parentMailbox);
final orphanMailbox = PresentationMailbox(MailboxId(Id('22')));
expect(node.addChild(orphanMailbox), null);
});
test('addChild add new mailbox with other parentId to a node should return null', () async {
final parentMailbox = PresentationMailbox(MailboxId(Id('1')));
final node = MailboxNode(parentMailbox);
final orphanMailbox = PresentationMailbox(MailboxId(Id('21')), parentId: MailboxId(Id('2')));
expect(node.addChild(orphanMailbox), null);
}); });
}); });
} }
@@ -11,14 +11,14 @@ void main() {
final expectedTree = MailboxTree( final expectedTree = MailboxTree(
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id('root'))), PresentationMailbox(MailboxId(Id('root'))),
child: [ childrenItems: [
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("1")), parentId: null), PresentationMailbox(MailboxId(Id("1")), parentId: null),
child: [ childrenItems: [
MailboxNode(PresentationMailbox(MailboxId(Id("1_1")), parentId: MailboxId(Id('1')))), MailboxNode(PresentationMailbox(MailboxId(Id("1_1")), parentId: MailboxId(Id('1')))),
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("1_2")), parentId: MailboxId(Id('1'))), PresentationMailbox(MailboxId(Id("1_2")), parentId: MailboxId(Id('1'))),
child: [ childrenItems: [
MailboxNode(PresentationMailbox(MailboxId(Id("1_2_1")), parentId: MailboxId(Id('1_2')))) MailboxNode(PresentationMailbox(MailboxId(Id("1_2_1")), parentId: MailboxId(Id('1_2'))))
] ]
), ),
@@ -26,12 +26,12 @@ void main() {
), ),
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("2")), parentId: null), PresentationMailbox(MailboxId(Id("2")), parentId: null),
child: [ childrenItems: [
MailboxNode(PresentationMailbox(MailboxId(Id("2_1")), parentId: MailboxId(Id('2'))), MailboxNode(PresentationMailbox(MailboxId(Id("2_1")), parentId: MailboxId(Id('2'))),
child: [ childrenItems: [
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("2_1_1")), parentId: MailboxId(Id('2_1'))), PresentationMailbox(MailboxId(Id("2_1_1")), parentId: MailboxId(Id('2_1'))),
child: [MailboxNode(PresentationMailbox(MailboxId(Id("2_1_1_1")), parentId: MailboxId(Id('2_1_1'))))] childrenItems: [MailboxNode(PresentationMailbox(MailboxId(Id("2_1_1_1")), parentId: MailboxId(Id('2_1_1'))))]
), ),
MailboxNode(PresentationMailbox(MailboxId(Id("2_1_2")), parentId: MailboxId(Id('2_1')))) MailboxNode(PresentationMailbox(MailboxId(Id("2_1_2")), parentId: MailboxId(Id('2_1'))))
] ]
@@ -41,16 +41,16 @@ void main() {
), ),
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("3")), parentId: null), PresentationMailbox(MailboxId(Id("3")), parentId: null),
child: [ childrenItems: [
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("3_1")), parentId: MailboxId(Id('3'))), PresentationMailbox(MailboxId(Id("3_1")), parentId: MailboxId(Id('3'))),
child: [ childrenItems: [
MailboxNode(PresentationMailbox(MailboxId(Id("3_1_1")), parentId: MailboxId(Id('3_1')))) MailboxNode(PresentationMailbox(MailboxId(Id("3_1_1")), parentId: MailboxId(Id('3_1'))))
] ]
), ),
MailboxNode( MailboxNode(
PresentationMailbox(MailboxId(Id("3_2")), parentId: MailboxId(Id('3'))), PresentationMailbox(MailboxId(Id("3_2")), parentId: MailboxId(Id('3'))),
child: [MailboxNode(PresentationMailbox(MailboxId(Id("3_2_1")), parentId: MailboxId(Id('3_2'))))] childrenItems: [MailboxNode(PresentationMailbox(MailboxId(Id("3_2_1")), parentId: MailboxId(Id('3_2'))))]
) )
] ]
), ),
@@ -103,8 +103,8 @@ void main() {
final generatedTree = await TreeBuilder().generateMailboxTree(testCase); final generatedTree = await TreeBuilder().generateMailboxTree(testCase);
expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems)); expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems!));
expect(generatedTree.root.childrenItems[2], equals(expectedTree.root.childrenItems[0])); expect(generatedTree.root.childrenItems![2], equals(expectedTree.root.childrenItems![0]));
}); });
test('list mailbox is not in ordered, parent come first, then grandpa, then children', () async { test('list mailbox is not in ordered, parent come first, then grandpa, then children', () async {
@@ -128,8 +128,8 @@ void main() {
final generatedTree = await TreeBuilder().generateMailboxTree(testCase); final generatedTree = await TreeBuilder().generateMailboxTree(testCase);
expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems)); expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems!));
expect(generatedTree.root.childrenItems[2], equals(expectedTree.root.childrenItems[0])); expect(generatedTree.root.childrenItems![2], equals(expectedTree.root.childrenItems![0]));
}); });
test('list mailbox is not in ordered, children come first, then grandpa, then parent', () async { test('list mailbox is not in ordered, children come first, then grandpa, then parent', () async {
@@ -153,8 +153,8 @@ void main() {
final generatedTree = await TreeBuilder().generateMailboxTree(testCase); final generatedTree = await TreeBuilder().generateMailboxTree(testCase);
expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems)); expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems!));
expect(generatedTree.root.childrenItems[2], equals(expectedTree.root.childrenItems[0])); expect(generatedTree.root.childrenItems![2], equals(expectedTree.root.childrenItems![0]));
}); });
test('list mailbox is not in ordered, children come first, then parent, then grandpa', () async { test('list mailbox is not in ordered, children come first, then parent, then grandpa', () async {
@@ -178,8 +178,8 @@ void main() {
final generatedTree = await TreeBuilder().generateMailboxTree(testCase); final generatedTree = await TreeBuilder().generateMailboxTree(testCase);
expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems)); expect(generatedTree.root.childrenItems, containsAll(expectedTree.root.childrenItems!));
expect(generatedTree.root.childrenItems[2], equals(expectedTree.root.childrenItems[0])); expect(generatedTree.root.childrenItems![2], equals(expectedTree.root.childrenItems![0]));
}); });
test('item have parent but not found in tree will become root child', () async { test('item have parent but not found in tree will become root child', () async {
@@ -204,7 +204,7 @@ void main() {
final generatedTree = await TreeBuilder().generateMailboxTree(testCase); final generatedTree = await TreeBuilder().generateMailboxTree(testCase);
expect(generatedTree.root.childrenItems.length, equals(4)); expect(generatedTree.root.childrenItems?.length, equals(4));
expect(generatedTree.root.childrenItems, expect(generatedTree.root.childrenItems,
contains(MailboxNode(PresentationMailbox(MailboxId(Id("e3_2_1")), parentId: MailboxId(Id('id42')))))); contains(MailboxNode(PresentationMailbox(MailboxId(Id("e3_2_1")), parentId: MailboxId(Id('id42'))))));
}); });
@@ -1,101 +0,0 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:jmap_dart_client/jmap/core/id.dart';
import 'package:jmap_dart_client/jmap/mail/mailbox/mailbox.dart';
import 'package:model/mailbox/presentation_mailbox.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_node.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/model/mailbox_tree.dart';
void main() {
group('test findNode in mailbox tree', () {
late MailboxTree? tree;
final level1Node = PresentationMailbox(MailboxId(Id('1')));
final level1Node1 = PresentationMailbox(MailboxId(Id('1_1')));
final level1Node2 = PresentationMailbox(MailboxId(Id('1_2')));
final level2Node1 = PresentationMailbox(MailboxId(Id('2_1')), parentId: MailboxId(Id('1')));
final level3Node1 = PresentationMailbox(MailboxId(Id('3_1')), parentId: MailboxId(Id('2_1')));
setUp(() {
tree = MailboxTree(MailboxNode.root());
tree!.insertNode(level1Node);
tree!.insertNode(level1Node1);
tree!.insertNode(level1Node2);
tree!.insertNode(level2Node1);
tree!.insertNode(level3Node1);
});
test('findNode should find a node in tree at level 1', () {
final node1 = tree!.findNode(MailboxId(Id('1')));
expect(node1!.item, equals(level1Node));
});
test('findNode should find a node in tree at level 2', () {
final node1 = tree!.findNode(MailboxId(Id('1_2')));
expect(node1!.item, equals(level1Node2));
});
test('findNode should find a leaf in tree', () {
final node1 = tree!.findNode(MailboxId(Id('3_1')));
expect(node1!.item, equals(level3Node1));
});
test('findNode should find a node which have children in tree', () {
final node1 = tree!.findNode(MailboxId(Id('2_1')));
expect(node1!.item, equals(level2Node1));
expect(node1.childrenItems.length, equals(1));
expect(node1.childrenItems[0].item, equals(level3Node1));
});
test('findNode should not find a node not in tree', () {
expect(tree!.findNode(MailboxId(Id('4_1'))), null);
});
tearDown(() {
tree = null;
});
});
group('test insertNode in mailbox tree', () {
test('insertNode should insert a node without parent at level 1', () {
final tree = MailboxTree(MailboxNode.root());
final mailbox = PresentationMailbox(MailboxId(Id("1")));
final insertedNode = tree.insertNode(mailbox);
expect(tree.root.childrenItems, contains(insertedNode));
});
test('insertNode should insert a node to correctly parent', () {
final tree = MailboxTree(MailboxNode.root());
final level1Node = PresentationMailbox(MailboxId(Id('1')));
final level1Node1 = PresentationMailbox(MailboxId(Id('1_1')));
final level1Node2 = PresentationMailbox(MailboxId(Id('1_2')));
tree.insertNode(level1Node);
tree.insertNode(level1Node1);
tree.insertNode(level1Node2);
final insertedNode = tree.insertNode(PresentationMailbox(MailboxId(Id('2_1_2')), parentId: MailboxId(Id('1_2'))));
final foundLevel1Node2 = tree.findNode(MailboxId(Id('1_2')));
expect(foundLevel1Node2?.childrenItems.length, equals(1));
expect(foundLevel1Node2?.childrenItems, contains(insertedNode));
});
test('insertNode should insert a node to root if parent not found', () {
final tree = MailboxTree(MailboxNode.root());
final level1Node = PresentationMailbox(MailboxId(Id('1')));
final level1Node1 = PresentationMailbox(MailboxId(Id('1_1')));
final level1Node2 = PresentationMailbox(MailboxId(Id('1_2')));
tree.insertNode(level1Node);
tree.insertNode(level1Node1);
tree.insertNode(level1Node2);
final insertedNode = tree.insertNode(PresentationMailbox(MailboxId(Id('1_1_1_exp')), parentId: MailboxId(Id('e1_2'))));
expect(tree.root.childrenItems.length, equals(4));
expect(tree.root.childrenItems.map((childNode) => childNode.item).toList(),
containsAll({level1Node, level1Node2, level1Node1, insertedNode!.item}));
});
});
}