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 }