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 }