1 /** 2 * Base tree nodes 3 * 4 * Copyright: © 2017 Andrey Kabylin 5 * License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 */ 7 8 module rpdl.node; 9 10 import std.stdio; 11 import std.array; 12 import std.container; 13 import std.traits; 14 import std.conv; 15 import std.algorithm.iteration : filter; 16 17 import rpdl.value; 18 import rpdl.accessors; 19 import rpdl.exception; 20 21 import gl3n.linalg; 22 23 /// Base tree node class 24 class Node { 25 const bool isRoot; /// If true then node is the root 26 27 this(in string name, in bool isRoot = false) { 28 this.p_name = name; 29 this.p_path = name; 30 this.isRoot = isRoot; 31 } 32 33 this(in string name, Node parent) { 34 this.p_name = name; 35 this.isRoot = false; 36 parent.insert(this); 37 } 38 39 @property string name() { return p_name; } 40 41 /// Path of node relative to the root 42 @property string path() { return p_path; } 43 @property Node parent() { return p_parent; } 44 @property Array!Node children() { return p_children; } 45 @property size_t length() { return p_children.length; } 46 47 @property void name(in string value) { 48 p_name = value; 49 updatePath(); 50 } 51 52 /// Retrieve child node by the `index` 53 Node getAtIndex(in size_t index) { 54 return p_children[index]; 55 } 56 57 /// Insert node to the children 58 void insert(Node object) { 59 p_children ~= object; 60 object.p_parent = this; 61 object.updatePath(); 62 } 63 64 /// Find node relative to this node by the path 65 Node getNode(in string relativePath) { 66 return findNodeByPath(relativePath, this); 67 } 68 69 Node optNode(in string path, Node defaultVal = null) { 70 Node node = findNodeByPath(path, getRootNode()); 71 72 if (node is null) 73 return defaultVal; 74 75 return node; 76 } 77 78 mixin Accessors; 79 80 protected: 81 string p_name; 82 string p_path; // Key for find node 83 84 Node p_parent; 85 package Node inherit; 86 Array!Node p_children; 87 88 /// Update path relative to `root` 89 void updatePath() { 90 assert(parent !is null); 91 p_path = parent.path == "" ? name : parent.path ~ "." ~ name; 92 93 foreach (Node child; p_children) 94 child.updatePath(); 95 } 96 97 private: 98 Node getRootNode() { 99 return this; 100 } 101 102 /// Recursively find node relative to the `node` 103 Node findNodeByPath(in string relativePath, Node node) { 104 assert(node !is null); 105 const absolutePath = isRoot ? relativePath : path ~ "." ~ relativePath; 106 107 foreach_reverse (Node child; node.children) { 108 if (child.path == absolutePath) 109 return child; 110 111 Node findNode = findNodeByPath(relativePath, child); 112 113 if (findNode !is null) { 114 return findNode; 115 } 116 } 117 118 if (node.inherit !is null) { 119 if (relativePath.length < node.name.length + 1) { 120 return null; 121 } 122 123 const inheritPath = relativePath.replace(node.name ~ ".", node.inherit.name ~ "."); 124 return findNodeByPath(inheritPath, node.inherit); 125 } 126 127 return null; 128 } 129 } 130 131 /// Represents parameter with the values in the object 132 class Parameter: Node { 133 this(in string name) { super(name); } 134 this(in string name, Node parent) { super(name, parent); } 135 } 136 137 /// Represents object with parameters and other objects 138 class ObjectNode: Node { 139 this(in string name) { super(name); } 140 this(in string name, Node parent) { super(name, parent); } 141 }