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