1 /**
2  * Copyright: Copyright (c) 2009-2011 Jacob Carlborg.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Oct 5, 2009
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module mambo.util.Reflection;
8 
9 import mambo.core..string;
10 import mambo.util.Ctfe;
11 
12 /**
13  * Returns the name of the given function
14  *
15  * Params:
16  *     func = the function alias to get the name of
17  *
18  * Returns: the name of the function
19  */
20 template functionNameOf (alias func)
21 {
22 	version(LDC)
23 		enum functionNameOf = (&func).stringof[1 .. $];
24 
25 	else
26 		enum functionNameOf = (&func).stringof[2 .. $];
27 }
28 
29 /**
30  * Returns the parameter names of the given function
31  *
32  * Params:
33  *     func = the function alias to get the parameter names of
34  *
35  * Returns: an array of strings containing the parameter names
36  */
37 template parameterNamesOf (alias func)
38 {
39 	enum parameterNamesOf = parameterNamesOfImpl!(func);
40 }
41 
42 /**
43  * Returns the parameter names of the given function
44  *
45  * Params:
46  *     func = the function alias to get the parameter names of
47  *
48  * Returns: an array of strings containing the parameter names
49  */
50 private string[] parameterNamesOfImpl (alias func) ()
51 {
52 	string funcStr = typeof(&func).stringof;
53 
54 	auto start = funcStr.indexOf('(');
55 	auto end = funcStr.indexOf(')');
56 
57 	enum firstPattern = ' ';
58 	enum secondPattern = ',';
59 
60 	funcStr = funcStr[start + 1 .. end];
61 
62 	if (funcStr == "")
63 		return null;
64 
65 	funcStr ~= secondPattern;
66 
67 	string token;
68 	string[] arr;
69 
70 	foreach (c ; funcStr)
71 	{
72 		if (c != firstPattern && c != secondPattern)
73 			token ~= c;
74 
75 		else
76 		{
77 			if (token)
78 				arr ~= token;
79 
80 			token = null;
81 		}
82 	}
83 
84 	if (arr.length == 1)
85 		return arr;
86 
87 	string[] result;
88 	bool skip = false;
89 
90 	foreach (str ; arr)
91 	{
92 		skip = !skip;
93 
94 		if (skip)
95 			continue;
96 
97 		result ~= str;
98 	}
99 
100 	return result;
101 }
102 
103 /**
104  * Helper function for callWithNamedArguments
105  *
106  * Returns:
107  */
108 private string buildFunction (alias func, string args) ()
109 {
110 	enum str = split(args);
111 	string[] params;
112 	string[] values;
113 	auto mixinString = functionNameOf!(func) ~ "(";
114 
115 	foreach (s ; str)
116 	{
117 		auto index = s.indexOf('=');
118 		params ~= s[0 .. index];
119 		values ~= s[index + 1 .. $];
120 	}
121 
122 	enum parameterNames = parameterNamesOf!(func);
123 
124 	foreach (i, s ; parameterNames)
125 	{
126 		auto index = params.indexOf(s);
127 
128 		if (index != params.length)
129 			mixinString ~= values[index] ~ ",";
130 	}
131 
132 	return mixinString[0 .. $ - 1] ~ ");";
133 }
134 
135 /**
136  * Calls the given function with named arguments
137  *
138  * Params:
139  *     func = an alias to the function to call
140  *     args = a string containing the arguments to call using this syntax: `arg2=value,arg1="value"`
141  */
142 void callWithNamedArguments (alias func, string args) ()
143 {
144 	mixin(buildFunction!(func, args));
145 }
146 
147 /**
148  * Evaluates to true if T has a instance method with the given name
149  *
150  * Params:
151  * 		T = the type of the class/struct
152  * 		method = the name of the method
153  */
154 template hasInstanceMethod (T, string method)
155 {
156 	enum hasInstanceMethod = is(typeof({
157 		T t;
158 		mixin("auto f = &t." ~ method ~ ";");
159 	}));
160 }
161 
162 /**
163  * Evaluates to true if T has a class method with the given name
164  *
165  * Params:
166  * 		T = the type of the class/struct
167  * 		method = the name of the method
168  */
169 template hasClassMethod (T, string method)
170 {
171 	enum hasClassMethod = is(typeof({
172 		mixin("auto f = &T." ~ method ~ ";");
173 	}));
174 }
175 
176 /**
177  * Evaluates to true if T has a either a class method or a instance method with the given name
178  *
179  * Params:
180  * 		T = the type of the class/struct
181  * 		method = the name of the method
182  */
183 template hasMethod (T, string method)
184 {
185 	enum hasMethod = hasClassMethod!(T, method) || hasInstanceMethod!(T, method);
186 }
187 
188 /**
189  * Evaluates to true if T has a field with the given name
190  *
191  * Params:
192  * 		T = the type of the class/struct
193  * 		field = the name of the field
194  */
195 template hasField (T, string field)
196 {
197 	enum hasField = hasFieldImpl!(T, field, 0);
198 }
199 
200 private template hasFieldImpl (T, string field, size_t i)
201 {
202 	static if (T.tupleof.length == i)
203 		enum hasFieldImpl = false;
204 
205 	else static if (nameOfFieldAt!(T, i) == field)
206 		enum hasFieldImpl = true;
207 
208 	else
209 		enum hasFieldImpl = hasFieldImpl!(T, field, i + 1);
210 }
211 
212 /**
213  * Evaluates to an array of strings containing the names of the fields in the given type
214  */
215 template fieldsOf (T)
216 {
217 	enum fieldsOf = fieldsOfImpl!(T, 0);
218 }
219 
220 /**
221  * Implementation for fieldsOf
222  *
223  * Returns: an array of strings containing the names of the fields in the given type
224  */
225 template fieldsOfImpl (T, size_t i)
226 {
227 	static if (T.tupleof.length == 0)
228 		enum fieldsOfImpl = [""];
229 
230 	else static if (T.tupleof.length - 1 == i)
231 		enum fieldsOfImpl = [nameOfFieldAt!(T, i)];
232 
233 	else
234 		enum fieldsOfImpl = nameOfFieldAt!(T, i) ~ fieldsOfImpl!(T, i + 1);
235 }
236 
237 /**
238  * Evaluates to the type of the field with the given name
239  *
240  * Params:
241  * 		T = the type of the class/struct
242  * 		field = the name of the field
243  */
244 template TypeOfField (T, string field)
245 {
246 	static assert(hasField!(T, field), "The given field \"" ~ field ~ "\" doesn't exist in the type \"" ~ T.stringof ~ "\"");
247 
248 	alias TypeOfFieldImpl!(T, field, 0) TypeOfField;
249 }
250 
251 private template TypeOfFieldImpl (T, string field, size_t i)
252 {
253 	static if (nameOfFieldAt!(T, i) == field)
254 		alias typeof(T.tupleof[i]) TypeOfFieldImpl;
255 
256 	else
257 		alias TypeOfFieldImpl!(T, field, i + 1) TypeOfFieldImpl;
258 }
259 
260 /**
261  * Evaluates to a string containing the name of the field at given position in the given type.
262  *
263  * Params:
264  * 		T = the type of the class/struct
265  * 		position = the position of the field in the tupleof array
266  */
267 template nameOfFieldAt (T, size_t position)
268 {
269     static assert (position < T.tupleof.length, format!(`The given position "`, position, `" is greater than the number of fields (`, T.tupleof.length, `) in the type "`, T, `"`));
270 
271 	enum nameOfFieldAt = __traits(identifier, T.tupleof[position]);
272 }
273 
274 /**
275  * Sets the given value to the filed with the given name
276  *
277  * Params:
278  *     t = an instance of the type that has the field
279  *     value = the value to set
280  */
281 void setValueOfField (T, U, string field) (ref T t, U value)
282 in
283 {
284 	static assert(hasField!(T, field), "The given field \"" ~ field ~ "\" doesn't exist in the type \"" ~ T.stringof ~ "\"");
285 }
286 body
287 {
288 	enum len = T.stringof.length;
289 
290 	foreach (i, dummy ; typeof(T.tupleof))
291 	{
292 		static if (f == nameOfFieldAt!(T, i))
293 		{
294 			t.tupleof[i] = value;
295 			break;
296 		}
297 	}
298 }
299 
300 /**
301  * Gets the value of the field with the given name
302  *
303  * Params:
304  *     t = an instance of the type that has the field
305  *
306  * Returns: the value of the field
307  */
308 U getValueOfField (T, U, string field) (T t)
309 in
310 {
311 	static assert(hasField!(T, field), "The given field \"" ~ field ~ "\" doesn't exist in the type \"" ~ T.stringof ~ "\"");
312 }
313 body
314 {
315 	enum len = T.stringof.length;
316 
317 	foreach (i, dummy ; typeof(T.tupleof))
318 	{
319 		static if (field == nameOfFieldAt!(T, i))
320 			return t.tupleof[i];
321 	}
322 
323 	assert(0);
324 }
325 
326 /**
327  * Gets all the class names in the given string of D code
328  *
329  * Params:
330  *     code = a string containg the code to get the class names from
331  *
332  * Returns: the class names
333  */
334 string[] getClassNames (string code) ()
335 {
336 	enum fileContent = code;
337 	enum classString = "class";
338 	bool foundPossibleClass;
339 	bool foundClass;
340 	string[] classNames;
341 	string className;
342 
343 	for (size_t i = 0; i < fileContent.length; i++)
344 	{
345 		final c = fileContent[i];
346 
347 		if (foundPossibleClass)
348 		{
349 			if (c == ' ' || c == '\n')
350 				foundClass = true;
351 
352 			foundPossibleClass = false;
353 		}
354 
355 		else if (foundClass)
356 		{
357 			if (c == '{')
358 			{
359 				classNames ~= className;
360 				foundClass = false;
361 				className = "";
362 			}
363 
364 			else if (c != ' ' && c != '\n')
365 				className ~= c;
366 		}
367 
368 		else
369 		{
370 			if (i + classString.length < fileContent.length)
371 			{
372 				if (fileContent[i .. i + classString.length] == classString)
373 				{
374 					if (i > 0)
375 					{
376 						if (fileContent[i - 1] == ' ' || fileContent[i - 1] == '\n' || fileContent[i - 1] == ';' || fileContent[i - 1] == '}')
377 						{
378 							foundPossibleClass = true;
379 							i += classString.length - 1;
380 							continue;
381 						}
382 					}
383 
384 					else
385 					{
386 						foundPossibleClass = true;
387 						i += classString.length - 1;
388 						continue;
389 					}
390 				}
391 			}
392 		}
393 	}
394 
395 	return classNames;
396 }
397 
398 /**
399  * Creates a new instance of class with the given name
400  *
401  * Params:
402  *     name = the fully qualified name of the class
403  *     args = the arguments to the constructor
404  *
405  * Returns: the newly created instance or null
406  */
407 T factory (T) (string name)
408 {
409 	auto classInfo = ClassInfo.find(name);
410 
411 	if (!classInfo)
412 		return null;
413 
414 	auto object = newInstance(classInfo);
415 
416 	if (classInfo.flags & 8 && classInfo.defaultConstructor is null)
417 	{
418 		auto o = cast(T) object;
419 
420 		static if (is(typeof(o._ctor(args))))
421 			return o._ctor(args);
422 
423 		else
424 			return null;
425 	}
426 
427 	else
428 	{
429 		if (classInfo.flags & 8 && classInfo.defaultConstructor !is null)
430 		{
431 			Object delegate () ctor;
432 			ctor.ptr = cast(void*) object;
433 			ctor.funcptr = cast(Object function()) classInfo.defaultConstructor;
434 
435 			return cast(T) ctor();
436 		}
437 
438 		else
439 			return cast(T) object;
440 	}
441 }
442 
443 private
444 {
445 	version (LDC)
446 		extern (C) Object _d_allocclass(in ClassInfo);
447 
448 	else
449 		extern (C) Object _d_newclass(in ClassInfo);
450 }
451 
452 /**
453  * Returns a new instnace of the class associated with the given class info.
454  *
455  * Params:
456  *     classInfo = the class info associated with the class
457  *
458  * Returns: a new instnace of the class associated with the given class info.
459  */
460 Object newInstance (in ClassInfo classInfo)
461 {
462 	version (LDC)
463 	{
464         Object object = _d_allocclass(classInfo);
465         const(void)[]defInitializer = classInfo.initializer();
466 
467         if (defInitializer !is null && defInitializer.length > 0)
468         {
469             if (defInitializer.ptr !is null)
470                 (cast(byte*) object)[0..defInitializer.length] = (cast(byte*)defInitializer.ptr)[0..defInitializer.length];
471             else
472                 (cast(byte*) object)[0..defInitializer.length] = 0;
473         }
474 
475         return object;
476 	}
477 
478 	else
479 		return _d_newclass(classInfo);
480 }
481 
482 /**
483  * Return a new instance of the class with the given name.
484  *
485  * Params:
486  *     name = the fully qualified name of the class
487  *
488  * Returns: a new instance or null if the class name could not be found
489  */
490 Object newInstance (string name)
491 {
492 	auto classInfo = ClassInfo.find(name);
493 
494 	if (!classInfo)
495 		return null;
496 
497 	return newInstance(classInfo);
498 }