1 | export default class ContextTree {
|
---|
2 | constructor(value) {
|
---|
3 | this.root = createNode(value || {});
|
---|
4 | }
|
---|
5 | set(path, value) {
|
---|
6 | const parent = this.getParent(path, true);
|
---|
7 | if (!parent) {
|
---|
8 | updateNode(this.root, value, null);
|
---|
9 | return;
|
---|
10 | }
|
---|
11 | const key = path[path.length - 1];
|
---|
12 | const {
|
---|
13 | children
|
---|
14 | } = parent;
|
---|
15 | if (children[key]) {
|
---|
16 | updateNode(children[key], value, parent);
|
---|
17 | return;
|
---|
18 | }
|
---|
19 | children[key] = createNode(value, parent);
|
---|
20 | }
|
---|
21 |
|
---|
22 | // Get the "best" node (node or nearest parent) and return its value.
|
---|
23 | get(path) {
|
---|
24 | path = path || [];
|
---|
25 | if (path.length < 1) {
|
---|
26 | return this.root.value;
|
---|
27 | }
|
---|
28 | let branch = this.root;
|
---|
29 | let child;
|
---|
30 | let token;
|
---|
31 | for (let i = 0; i < path.length; i += 1) {
|
---|
32 | token = path[i];
|
---|
33 | child = branch.children;
|
---|
34 | if (!child[token]) {
|
---|
35 | break;
|
---|
36 | }
|
---|
37 | branch = child[token];
|
---|
38 | }
|
---|
39 | return branch && branch.protoValue;
|
---|
40 | }
|
---|
41 | getParent(path, ensureExists) {
|
---|
42 | if (!path || path.length < 1) {
|
---|
43 | return null;
|
---|
44 | }
|
---|
45 | if (path.length < 2) {
|
---|
46 | return this.root;
|
---|
47 | }
|
---|
48 | return path.slice(0, -1).reduce((branch, token) => {
|
---|
49 | if (!branch) {
|
---|
50 | return branch;
|
---|
51 | }
|
---|
52 | const {
|
---|
53 | children
|
---|
54 | } = branch;
|
---|
55 | if (!children[token] && ensureExists) {
|
---|
56 | children[token] = createNode(null, branch);
|
---|
57 | }
|
---|
58 | return children[token];
|
---|
59 | }, this.root);
|
---|
60 | }
|
---|
61 | }
|
---|
62 |
|
---|
63 | // =========================
|
---|
64 | // Utilities
|
---|
65 | // =========================
|
---|
66 |
|
---|
67 | function createNode(value, parent) {
|
---|
68 | return updateNode({
|
---|
69 | children: {}
|
---|
70 | }, value, parent);
|
---|
71 | }
|
---|
72 | function updateNode(node, value, parent) {
|
---|
73 | node.value = value || {};
|
---|
74 | node.protoValue = parent ? {
|
---|
75 | ...parent.protoValue,
|
---|
76 | ...node.value
|
---|
77 | } : node.value;
|
---|
78 | Object.keys(node.children).forEach(prop => {
|
---|
79 | const child = node.children[prop];
|
---|
80 | node.children[prop] = updateNode(child, child.value, node);
|
---|
81 | });
|
---|
82 | return node;
|
---|
83 | } |
---|