1 /**
2  * Mixin for retrieving values from the parsed tree by path
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.accessors;
9 
10 /**
11  * Mixin for retrieving values from the parsed tree by path.
12  * To access to variables you can use `get` or `opt` methods e.g. `getParameter` will
13  * return `rpdl.node.Parameter`, i.e. function name builds from prefix (get or opt) and
14  * type name. If method starts with `get`, then method can throw an exception
15  * if value will not find by the path. If you use `opt`, then exception will not thrown
16  * if value didn't found, also you can set default value for value which not found.
17  * If value found but has wrong type you will get `WrongNodeType` exception.
18  */
19 mixin template Accessors() {
20     alias getObjectNode = getTypedNode!(ObjectNode);
21     alias getParameterNode = getTypedNode!(Parameter);
22     alias getValueNode = getTypedNode!(Value);
23     alias getNumberValueNode = getTypedNode!(NumberValue);
24     alias getStringValueNode = getTypedNode!(StringValue);
25     alias getIdentifierValueNode = getTypedNode!(IdentifierValue);
26     alias getBooleanValueNode = getTypedNode!(BooleanValue);
27     alias getArrayValueNode = getTypedNode!(ArrayValue);
28 
29     alias optObjectNode = optTypedNode!(ObjectNode);
30     alias optParameterNode = optTypedNode!(Parameter);
31     alias optValueNode = optTypedNode!(Value);
32     alias optNumberValueNode = optTypedNode!(NumberValue);
33     alias optStringValueNode = optTypedNode!(StringValue);
34     alias optIdentifierValueNode = optTypedNode!(IdentifierValue);
35     alias optBooleanValueNode = optTypedNode!(BooleanValue);
36     alias optArrayValueNode = optTypedNode!(ArrayValue);
37 
38     alias getNumber = getTypedValue!(float, NumberValue);
39     alias getBoolean = getTypedValue!(bool, BooleanValue);
40     alias getString = getTypedValue!(string, StringValue);
41     alias getIdentifier = getTypedValue!(string, IdentifierValue);
42 
43     alias optNumber = optTypedValue!(float, NumberValue);
44     alias optBoolean = optTypedValue!(bool, BooleanValue);
45     alias optString = optTypedValue!(string, StringValue);
46     alias optIdentifier = optTypedValue!(string, IdentifierValue);
47 
48     dstring getUTF32String(in string path) {
49         return getTypedNode!(StringValue)(path).utf32Value;
50     }
51 
52     int getInteger(in string path) {
53         return to!int(getNumber(path));
54     }
55 
56     dstring optUTF32String(in string path, dstring defaultVal = dstring.init) {
57         StringValue node = optTypedNode!(StringValue)(path, null);
58 
59         if (node is null)
60             return defaultVal;
61 
62         return node.utf32Value;
63     }
64 
65     int optInteger(in string path, int defaultVal = 0) {
66         return to!int(optNumber(path, to!float(defaultVal)));
67     }
68 
69     alias getVec2f = getVecValue!(float, 2);
70     alias getVec3f = getVecValue!(float, 3);
71     alias getVec4f = getVecValue!(float, 4);
72     alias getVec2i = getVecValue!(int, 2);
73     alias getVec3i = getVecValue!(int, 3);
74     alias getVec4i = getVecValue!(int, 4);
75     alias getVec2ui = getVecValue!(uint, 2);
76     alias getVec3ui = getVecValue!(uint, 3);
77     alias getVec4ui = getVecValue!(uint, 4);
78 
79     alias optVec2f = optVecValue!(float, 2);
80     alias optVec3f = optVecValue!(float, 3);
81     alias optVec4f = optVecValue!(float, 4);
82     alias optVec2i = optVecValue!(int, 2);
83     alias optVec3i = optVecValue!(int, 3);
84     alias optVec4i = optVecValue!(int, 4);
85     alias optVec2ui = optVecValue!(uint, 2);
86     alias optVec3ui = optVecValue!(uint, 3);
87     alias optVec4ui = optVecValue!(uint, 4);
88 
89     /**
90      * Retrieve normilized color to 1 i.e. r/255, g/255, b/255, a/100.
91      *
92      * Returns:
93      *     `vec4f` value contains inside the node
94      *     if `rpdl.node.Node` has 3 components, then `alpha` will set to 1.
95      */
96     vec4 getNormColor(in string path) {
97         vec4 color;
98 
99         try {
100             color = getVec4f(path);
101         } catch(WrongNodeType) {
102             try {
103                 vec3 color3 = getVec3f(path);
104                 color = vec4(color3, 100.0f);
105             } catch(WrongNodeType) {
106                 throw new WrongNodeType(path, "color(vec4 or vec3)");
107             }
108         }
109 
110         color = vec4(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 100.0f);
111         return color;
112     }
113 
114     /**
115      * Retrieve enum with type `T` value from found node by `path`
116      * See_also: `optEnum`, `ufcsGetEnum`, `ufcsOptEnum`
117      */
118     T getEnum(T)(in string path) {
119         const string val = getIdentifier(path);
120 
121         foreach (immutable enumItem; [EnumMembers!T]) {
122             if (to!string(enumItem) == val) {
123                 return enumItem;
124             }
125         }
126 
127         throw new WrongNodeType(path, T.stringof);
128     }
129 
130     /**
131      * Retrieve optional enum with type `T` value from found node by `path`
132      * if node was not found then it will return `defaultVal`
133      * See_also: `getEnum`, `ufcsGetEnum`, `ufcsOptEnum`
134      */
135     T optEnum(T)(in string path, in T defaultVal = T.init) {
136         try {
137             return getEnum!(T)(path);
138         } catch (NotFoundException) {
139             return defaultVal;
140         }
141     }
142 
143 // Helper methods for access to nodes and values by path -------------------------------------------
144 
145     /**
146      * Find typed node by `path`, node should be inherited from `T`.
147      * See_also: `optTypedNode`, `getTypedValue`, `optTypedValue`
148      */
149     private T getTypedNode(T : Node)(in string path) {
150         Node node = findNodeByPath(path, getRootNode());
151 
152         if (node is null)
153             throw new NotFoundException("Node with path \"" ~ path ~ "\" not found");
154 
155         T object = cast(T)(node);
156 
157         if (object is null)
158             throw new WrongNodeType(path, T.stringof);
159 
160         return cast(T)(node);
161     }
162 
163     /**
164      * Get node by `path` and retrieve value from this node.
165      * Node should be ingerited from `N` and value should be inherited from `T`.
166      * See_also: `optTypedNode`, `getTypedNode`, `optTypedValue`
167      */
168     private T getTypedValue(T, N : Node)(in string path) {
169         return getTypedNode!N(path).value;
170     }
171 
172     /**
173      * Get node by `path`, node should be inherited from `T`.
174      * If node was not found then it will return `defaultVal`.
175      * See_also: `optTypedNode`, `getTypedValue`, `optTypedValue`
176      */
177     private T optTypedNode(T : Node)(in string path, T defaultVal) {
178         Node node = findNodeByPath(path, getRootNode());
179 
180         if (node is null)
181             return defaultVal;
182 
183         T object = cast(T)(node);
184 
185         if (object is null)
186             throw new WrongNodeType(path, T.stringof);
187 
188         return cast(T)(node);
189     }
190 
191     /**
192      * Get node by `path` and retrieve value from this node.
193      * Node should be ingerited from `N` and value should be inherited from `T`.
194      * If node was not found then it will return `defaultVal`.
195      * See_also: `getTypedValue`, `getTypedNode`, `optTypedNode`
196      */
197     private T optTypedValue(T, N : Node)(in string path, T defaultVal = T.init) {
198         N node = optTypedNode!N(path, null);
199 
200         if (node is null)
201             return defaultVal;
202 
203         return node.value;
204     }
205 
206     /**
207      * This method retrieves vector value from node by `path`, size of vector is `n`
208      * and type of their components is `T`.
209      *
210      * See_also: `getVecValueFromNode`, `optVecValue`
211      */
212     private Vector!(T, n) getVecValue(T, int n)(in string path) {
213         Node node = getNode(path);
214 
215         if (node is null)
216             throw new NotFoundException("Node with path \"" ~ path ~ "\" not found");
217 
218         return getVecValueFromNode!(T, n)(path, node);
219     }
220 
221     /**
222      * This method retrieves vector value from particular `node`. Size of vector is `n`
223      * and type of their components is `T`.
224      * If node was not found then method will return `defaultVal`.
225      * See_also: `getVecValue`, `optVecValue`
226      */
227     private Vector!(T, n) getVecValueFromNode(T, int n)(in string path, Node node) {
228         if (node.length != n)
229             throw new WrongNodeType(path, T.stringof);
230 
231         NumberValue[n] vectorComponents;
232         T[n] values;
233 
234         for (int i = 0; i < n; ++i) {
235             vectorComponents[i] = cast(NumberValue) node.getAtIndex(i);
236 
237             if (vectorComponents[i] is null)
238                 throw new WrongNodeType(path, T.stringof);
239 
240             values[i] = to!T(vectorComponents[i].value);
241         }
242 
243         return Vector!(T, n)(values);
244     }
245 
246     /**
247      * This method retrieves vector value from node by `path`, size of vector is `n`
248      * and type of their components is `T`.
249      * If node was not found then method will return `defaultVal`.
250      * See_also: `getVecValue`, `getVecValueFromNode`
251      */
252     private Vector!(T, n) optVecValue(T, int n)(in string path,
253         Vector!(T, n) defaultVal = Vector!(T, n).init)
254     {
255         Node node = findNodeByPath(path, getRootNode());
256 
257         if (node is null)
258             return defaultVal;
259 
260         return getVecValueFromNode!(T, n)(path, node);
261     }
262 }
263 
264 import rpdl.exception;
265 import rpdl.node;
266 
267 /// UFCS method for `Accessors.getEnum`
268 T ufcsGetEnum(T)(Node node, in string path) {
269     return node.getEnum!(T)(path);
270 }
271 
272 /// UFCS method for `Accessors.optEnum`
273 T ufcsOptEnum(T)(Node node, in string path, in T defaultVal = T.init) {
274     return node.optEnum!(T)(path, defaultVal);
275 }