[d24f17c] | 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 | } |
---|