From 37c5320e3389cdc6593efc9dd89a9783635f6ddb Mon Sep 17 00:00:00 2001 From: vsavkin Date: Fri, 20 May 2016 13:56:52 -0700 Subject: [PATCH] feat: implement Tree --- modules/@angular/router/src/tree.ts | 69 +++++++++++++++++++++++ modules/@angular/router/test/tree.spec.ts | 50 ++++++++++++++++ modules/@angular/router/tsconfig.json | 1 + 3 files changed, 120 insertions(+) create mode 100644 modules/@angular/router/src/tree.ts create mode 100644 modules/@angular/router/test/tree.spec.ts diff --git a/modules/@angular/router/src/tree.ts b/modules/@angular/router/src/tree.ts new file mode 100644 index 0000000000..ecd3c34cee --- /dev/null +++ b/modules/@angular/router/src/tree.ts @@ -0,0 +1,69 @@ +export class Tree { + /** @internal */ + _root: TreeNode; + + constructor(root: TreeNode) { this._root = root; } + + get root(): T { return this._root.value; } + + parent(t: T): T { + const p = this.pathFromRoot(t); + return p.length > 1 ? p[p.length - 2] : null; + } + + children(t: T): T[] { + const n = _findNode(t, this._root); + return n ? n.children.map(t => t.value) : null; + } + + firstChild(t: T): T { + const n = _findNode(t, this._root); + return n && n.children.length > 0 ? n.children[0].value : null; + } + + pathFromRoot(t: T): T[] { return _findPath(t, this._root, []).map(s => s.value); } + + contains(tree: Tree): boolean { return _contains(this._root, tree._root); } +} + +export function rootNode(tree: Tree): TreeNode { + return tree._root; +} + +function _findNode(expected: T, c: TreeNode): TreeNode { + if (expected === c.value) return c; + for (let cc of c.children) { + const r = _findNode(expected, cc); + if (r) return r; + } + return null; +} + +function _findPath(expected: T, c: TreeNode, collected: TreeNode[]): TreeNode[] { + collected.push(c); + if (expected === c.value) return collected; + + for (let cc of c.children) { + const cloned = collected.slice(0); + const r = _findPath(expected, cc, cloned); + if (r) return r; + } + + return null; +} + +function _contains(tree: TreeNode, subtree: TreeNode): boolean { + if (tree.value !== subtree.value) return false; + + for (let subtreeNode of subtree.children) { + const s = tree.children.filter(child => child.value === subtreeNode.value); + if (s.length === 0) return false; + if (!_contains(s[0], subtreeNode)) return false; + } + + return true; +} + +export class TreeNode { + constructor(public value: T, public children: TreeNode[]) {} +} \ No newline at end of file diff --git a/modules/@angular/router/test/tree.spec.ts b/modules/@angular/router/test/tree.spec.ts new file mode 100644 index 0000000000..048a117db8 --- /dev/null +++ b/modules/@angular/router/test/tree.spec.ts @@ -0,0 +1,50 @@ +import {Tree, TreeNode} from '../src/tree'; + +describe('tree', () => { + it("should return the root of the tree", () => { + const t = new Tree(new TreeNode(1, [])); + expect(t.root).toEqual(1); + }); + + it("should return the parent of a node", () => { + const t = new Tree(new TreeNode(1, [new TreeNode(2, [])])); + expect(t.parent(1)).toEqual(null); + expect(t.parent(2)).toEqual(1); + }); + + it("should return the children of a node", () => { + const t = new Tree(new TreeNode(1, [new TreeNode(2, [])])); + expect(t.children(1)).toEqual([2]); + expect(t.children(2)).toEqual([]); + }); + + it("should return the first child of a node", () => { + const t = new Tree(new TreeNode(1, [new TreeNode(2, [])])); + expect(t.firstChild(1)).toEqual(2); + expect(t.firstChild(2)).toEqual(null); + }); + + it("should return the path to the root", () => { + const t = new Tree(new TreeNode(1, [new TreeNode(2, [])])); + expect(t.pathFromRoot(2)).toEqual([1, 2]); + }); + + describe("contains", () => { + it("should work", () => { + const tree = new Tree( + new TreeNode(1, [new TreeNode(2, []), new TreeNode(3, [])])); + const subtree1 = new Tree(new TreeNode(1, [])); + const subtree2 = new Tree(new TreeNode(1, [new TreeNode(2, [])])); + const subtree3 = new Tree(new TreeNode(1, [new TreeNode(3, [])])); + const notSubtree1 = new Tree(new TreeNode(1, [new TreeNode(4, [])])); + const notSubtree2 = new Tree( + new TreeNode(1, [new TreeNode(2, [new TreeNode(4, [])])])); + + expect(tree.contains(subtree1)).toEqual(true); + expect(tree.contains(subtree2)).toEqual(true); + expect(tree.contains(subtree3)).toEqual(true); + expect(tree.contains(notSubtree1)).toEqual(false); + expect(tree.contains(notSubtree2)).toEqual(false); + }); + }); +}); diff --git a/modules/@angular/router/tsconfig.json b/modules/@angular/router/tsconfig.json index 1517df04cc..096b7dd3ac 100644 --- a/modules/@angular/router/tsconfig.json +++ b/modules/@angular/router/tsconfig.json @@ -14,6 +14,7 @@ }, "files": [ "src/router.ts", + "test/tree.spec.ts", "test/router.spec.ts", "typings/main.d.ts" ]