engineDoc.cpp
Engine/source/console/engineDoc.cpp
Documentation generator for the current TorqueScript-based engine API.
Public Defines
define
Public Variables
Used to track unique groups encountered during the dump process.
Public Functions
DefineEngineFunction(dumpEngineDocs , bool , (const char *outputFile) , "Dumps the engine scripting documentation <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> overwriting any existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n</a>" "@param outputFile The relative or absolute output <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> path and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">name.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">successful.\n</a>" "@ingroup Console" )
dumpClasses(Stream & stream)
dumpClassFooter(Stream & stream)
dumpClassHeader(Stream & stream, const char * usage, const char * className, const char * superClassName)
dumpClassMember(Stream & stream, const AbstractClassRep::Field & field)
bool
dumpEngineDocs(const char * outputFile)
dumpEnum(Stream & stream, const EngineTypeInfo * type)
dumpFragment(Stream & stream, ConsoleDocFragment * fragment)
dumpFunction(Stream & stream, bool isClassMethod, Namespace::Entry * entry)
dumpGroupEnd(Stream & stream)
dumpGroupStart(Stream & stream, const char * aName, const char * aDocs)
dumpNamespaceEntries(Stream & stream, Namespace * g, bool callbacks)
dumpVariable(Stream & stream, Dictionary::Entry * entry, const char * inClass)
dumpVariables(Stream & stream, const char * inClass)
Detailed Description
Documentation generator for the current TorqueScript-based engine API.
Be aware that this generator is solely for the legacy console system and is and will not be useful to the new engine API system. It will go away when the console interop is removed.
Public Defines
USE_UNDOCUMENTED_GROUP()
Public Variables
HashTable< String, U32 > smDocGroups
Used to track unique groups encountered during the dump process.
Public Functions
DefineEngineFunction(dumpEngineDocs , bool , (const char *outputFile) , "Dumps the engine scripting documentation <a href="/coding/file/cmdgram_8cpp/#cmdgram_8cpp_1a5bafda9519252aa2d0fd038153f77dca">to</a> the specified <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> overwriting any existing <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">content.\n</a>" "@param outputFile The relative or absolute output <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1a702945180aa732857b380a007a7e2a21">file</a> path and <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">name.\n</a>" "@return Returns true <a href="/coding/file/tsmeshintrinsics_8cpp/#tsmeshintrinsics_8cpp_1a4e4fa7e3399708e0777b5308db01278c">if</a> <a href="/coding/file/cmdscan_8cpp/#cmdscan_8cpp_1aeab71244afb687f16d8c4f5ee9d6ef0e">successful.\n</a>" "@ingroup Console" )
dumpClasses(Stream & stream)
dumpClassFooter(Stream & stream)
dumpClassHeader(Stream & stream, const char * usage, const char * className, const char * superClassName)
dumpClassMember(Stream & stream, const AbstractClassRep::Field & field)
dumpDoc(Stream & stream, const char * text, bool checkUngrouped)
dumpEngineDocs(const char * outputFile)
dumpEnum(Stream & stream, const EngineTypeInfo * type)
dumpEnums(Stream & stream)
dumpFragment(Stream & stream, ConsoleDocFragment * fragment)
dumpFunction(Stream & stream, bool isClassMethod, Namespace::Entry * entry)
dumpGroupEnd(Stream & stream)
dumpGroupStart(Stream & stream, const char * aName, const char * aDocs)
dumpNamespaceEntries(Stream & stream, Namespace * g, bool callbacks)
dumpVariable(Stream & stream, Dictionary::Entry * entry, const char * inClass)
dumpVariables(Stream & stream, const char * inClass)
1 2//----------------------------------------------------------------------------- 3// Copyright (c) 2012 GarageGames, LLC 4// 5// Permission is hereby granted, free of charge, to any person obtaining a copy 6// of this software and associated documentation files (the "Software"), to 7// deal in the Software without restriction, including without limitation the 8// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9// sell copies of the Software, and to permit persons to whom the Software is 10// furnished to do so, subject to the following conditions: 11// 12// The above copyright notice and this permission notice shall be included in 13// all copies or substantial portions of the Software. 14// 15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 21// IN THE SOFTWARE. 22//----------------------------------------------------------------------------- 23 24#include "platform/platform.h" 25#include "console/console.h" 26 27#include "console/engineAPI.h" 28#include "core/stream/fileStream.h" 29#include "console/consoleInternal.h" 30#include "console/compiler.h" 31 32#define USE_UNDOCUMENTED_GROUP 33 34/// @file 35/// Documentation generator for the current TorqueScript-based engine API. 36/// 37/// Be aware that this generator is solely for the legacy console system and 38/// is and will not be useful to the new engine API system. It will go away 39/// when the console interop is removed. 40 41 42/// Used to track unique groups encountered during 43/// the dump process. 44static HashTable<String,U32> smDocGroups; 45 46 47static void dumpDoc( Stream& stream, const char* text, bool checkUngrouped = true ) 48{ 49 // Extract brief. 50 51 String brief; 52 53 if( text ) 54 { 55 const char* briefTag = dStrstr( text, "@brief" ); 56 if( !briefTag ) 57 { 58 const char* newline = dStrchr( text, '\n' ); 59 if( newline ) 60 { 61 brief = String( text, newline - text ); 62 text = newline + 1; 63 } 64 else 65 { 66 brief = text; 67 text = NULL; 68 } 69 } 70 } 71 72 // Write doc comment. 73 74 if( !brief.isEmpty() ) 75 { 76 stream.writeText( "@brief " ); 77 stream.writeText( brief ); 78 stream.writeText( "\r\n\r\n" ); 79 } 80 81 if( text ) 82 stream.writeText( text ); 83#ifdef USE_UNDOCUMENTED_GROUP 84 if( checkUngrouped && ( !text || !dStrstr( text, "@ingroup" ) ) ) 85 { 86 smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); 87 stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" ); 88 } 89#endif 90} 91 92static void dumpFragment( Stream& stream, ConsoleDocFragment* fragment ) 93{ 94 if( !fragment->mText || !fragment->mText[ 0 ] ) 95 return; 96 97 // Emit doc text in comment. 98 99 stream.writeText( "/*!\r\n" ); 100 stream.writeText( fragment->mText ); 101 stream.writeText( "*/\r\n\r\n" ); 102 103 // Emit definition, if any. 104 105 if( fragment->mDefinition ) 106 { 107 stream.writeText( fragment->mDefinition ); 108 stream.writeText( "\r\n" ); 109 } 110} 111 112static void dumpVariable( Stream& stream, 113 Dictionary::Entry* entry, 114 const char* inClass = NULL ) 115{ 116 // Skip variables defined in script. 117 118 if( entry->value.type < 0 ) 119 return; 120 121 // Skip internals... don't export them. 122 if ( entry->mUsage && 123 ( dStrstr( entry->mUsage, "@hide" ) || dStrstr( entry->mUsage, "@internal" ) ) ) 124 return; 125 126 // Split up qualified name. 127 128 Vector< String> nameComponents; 129 String( entry->name ).split( "::", nameComponents ); 130 if( !nameComponents.size() ) // Safety check. 131 return; 132 133 // Match filter. 134 135 if( inClass ) 136 { 137 // Make sure first qualifier in name components is a 138 // namespace qualifier matching the given class name. 139 140 if( nameComponents.size() <= 1 || dStricmp( nameComponents.first().c_str() + 1, inClass ) != 0 ) // Skip '$'. 141 return; 142 } 143 else 144 { 145 // Make sure, this is *not* in a class namespace. 146 147 if( nameComponents.size() > 1 && Con::lookupNamespace( nameComponents.first().c_str() + 1 )->mClassRep ) 148 return; 149 } 150 151 // Skip variables for which we can't decipher their type. 152 153 ConsoleBaseType* type = ConsoleBaseType::getType( entry->value.type ); 154 if( !type ) 155 { 156 Con::errorf( "Can't find type for variable '%s'", entry->name ); 157 return; 158 } 159 160 // Write doc comment. 161 162 stream.writeText( "/*!\r\n" ); 163 164 if( !inClass ) 165 { 166 stream.writeText( "@var " ); 167 stream.writeText( type->getTypeClassName() ); 168 stream.writeText( " " ); 169 stream.writeText( entry->name ); 170 stream.writeText( ";\r\n" ); 171 } 172 173 dumpDoc( stream, entry->mUsage ); 174 175 stream.writeText( "*/\r\n" ); 176 177 // Write definition. 178 179 const U32 numNameComponents = nameComponents.size(); 180 if( !inClass && numNameComponents > 1 ) 181 for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i ) 182 { 183 stream.writeText( "namespace " ); 184 stream.writeText( nameComponents[ i ] ); 185 stream.writeText( " { " ); 186 } 187 188 if( inClass ) 189 stream.writeText( "static " ); 190 191 if( entry->mIsConstant ) 192 stream.writeText( "const " ); 193 194 stream.writeText( type->getTypeClassName() ); 195 stream.writeText( " " ); 196 stream.writeText( nameComponents.last() ); 197 stream.writeText( ";" ); 198 199 if( !inClass && numNameComponents > 1 ) 200 for( U32 i = 0; i < ( numNameComponents - 1 ); ++ i ) 201 stream.writeText( " } " ); 202 203 stream.writeText( "\r\n" ); 204} 205 206static void dumpVariables( Stream& stream, const char* inClass = NULL ) 207{ 208 const U32 hashTableSize = gEvalState.globalVars.hashTable->size; 209 for( U32 i = 0; i < hashTableSize; ++ i ) 210 for( Dictionary::Entry* entry = gEvalState.globalVars.hashTable->data[ i ]; entry != NULL; entry = entry->nextEntry ) 211 dumpVariable( stream, entry, inClass ); 212} 213 214static void dumpFunction( Stream &stream, 215 bool isClassMethod, 216 Namespace::Entry* entry ) 217{ 218 String doc = entry->getDocString().trim(); 219 String prototype = entry->getPrototypeString().trim(); 220 221 // If the doc string contains @hide, skip this function. 222 223 if( dStrstr( doc.c_str(), "@hide" ) || dStrstr( doc.c_str(), "@internal" ) ) 224 return; 225 226 // Make sure we have a valid function prototype. 227 228 if( prototype.isEmpty() ) 229 { 230 Con::errorf( "Function '%s::%s' has no prototype!", entry->mNamespace->mName, entry->mFunctionName ); 231 return; 232 } 233 234 // See if it's a static method. 235 236 bool isStaticMethod = false; 237 if( entry->mHeader ) 238 isStaticMethod = entry->mHeader->mIsStatic; 239 240 // Emit the doc comment. 241 242 if( !doc.isEmpty() ) 243 { 244 stream.writeText( "/*!\r\n" ); 245 246 // If there's no @brief, take the first line of the doc text body 247 // as the description. 248 249 const char* brief = dStrstr( doc, "@brief" ); 250 if( !brief ) 251 { 252 String briefStr = entry->getBriefDescription( &doc ); 253 254 briefStr.trim(); 255 if( !briefStr.isEmpty() ) 256 { 257 stream.writeText( "@brief " ); 258 stream.writeText(briefStr); 259 stream.writeText( "\r\n\r\n" ); 260 } 261 } 262 263 stream.writeText( doc ); 264 265 // Emit @ingroup if it's not a class method. 266 267 if ( !isClassMethod && !isStaticMethod ) // Extra static method check for static classes (which will come out as non-class namespaces). 268 { 269 const char *group = dStrstr( doc, "@ingroup" ); 270 if( group ) 271 { 272 char groupName[ 256 ] = { 0 }; 273 dSscanf( group, "@ingroup %s", groupName ); 274 smDocGroups.insertUnique( groupName, 0 ); 275 } 276#ifdef USE_UNDOCUMENTED_GROUP 277 else 278 { 279 smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); 280 stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" ); 281 } 282#endif 283 } 284 285 stream.writeText( "*/\r\n" ); 286 } 287#ifdef USE_UNDOCUMENTED_GROUP 288 else if( !isClassMethod ) 289 { 290 smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); 291 stream.writeText( "/*! UNDOCUMENTED!\r\n@ingroup UNDOCUMENTED\r\n */\r\n" ); 292 } 293#endif 294 295 if( isStaticMethod ) 296 stream.writeText( "static " ); 297 298 stream.writeText( prototype ); 299 stream.writeText( ";\r\n" ); 300} 301 302static void dumpNamespaceEntries( Stream &stream, Namespace *g, bool callbacks = false ) 303{ 304 /// Only print virtual on methods that are members of 305 /// classes as this allows doxygen to properly do overloads. 306 const bool isClassMethod = g->mClassRep != NULL; 307 308 // Go through all the entries in the namespace. 309 for ( Namespace::Entry *ewalk = g->mEntryList; ewalk; ewalk = ewalk->mNext ) 310 { 311 S32 eType = ewalk->mType; 312 313 // We do not dump script defined functions... only engine exports. 314 if( eType == Namespace::Entry::ConsoleFunctionType 315 || eType == Namespace::Entry::GroupMarker ) 316 continue; 317 318 if( eType == Namespace::Entry::ScriptCallbackType ) 319 { 320 if( !callbacks ) 321 continue; 322 } 323 else if( callbacks ) 324 continue; 325 326 dumpFunction( stream, isClassMethod, ewalk ); 327 } 328} 329 330static void dumpClassHeader( Stream &stream, 331 const char *usage, 332 const char *className, 333 const char *superClassName ) 334{ 335 if ( usage ) 336 { 337 stream.writeText( "/*!\r\n" ); 338 stream.writeText( usage ); 339 340 const char *group = dStrstr( usage, "@ingroup" ); 341 if ( group ) 342 { 343 char groupName[256] = { 0 }; 344 dSscanf( group, "@ingroup %s", groupName ); 345 smDocGroups.insertUnique( groupName, 0 ); 346 } 347#ifdef USE_UNDOCUMENTED_GROUP 348 else 349 { 350 smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); 351 stream.writeText( "\r\n@ingroup UNDOCUMENTED\r\n" ); 352 } 353#endif 354 355 stream.writeText( "\r\n*/\r\n" ); 356 } 357 else 358 { 359 // No documentation string. Check whether ther is a separate 360 // class doc fragement. 361 362 bool haveClassDocFragment = false; 363 if( className ) 364 { 365 char buffer[ 1024 ]; 366 dSprintf( buffer, sizeof( buffer ), "@class %s", className ); 367 368 for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; 369 fragment != NULL; fragment = fragment->mNext ) 370 if( !fragment->mClass && dStrstr( fragment->mText, buffer ) != NULL ) 371 { 372 haveClassDocFragment = true; 373 break; 374 } 375 } 376#ifdef USE_UNDOCUMENTED_GROUP 377 if( !haveClassDocFragment ) 378 { 379 smDocGroups.insertUnique( "UNDOCUMENTED", 0 ); 380 stream.writeText( "/*! UNDOCUMENTED!\r\n@ingroup UNDOCUMENTED\r\n */\r\n" ); 381 } 382#endif 383 } 384 385 // Print out appropriate class header 386 if ( superClassName ) 387 stream.writeText( String::ToString( "class %s : public %s {\r\npublic:\r\n", className, superClassName ? superClassName : "" ) ); 388 else if ( className ) 389 stream.writeText( String::ToString( "class %s {\r\npublic:\r\n", className ) ); 390 else 391 stream.writeText( "namespace {\r\n" ); 392} 393 394static void dumpClassMember( Stream &stream, 395 const AbstractClassRep::Field& field ) 396{ 397 stream.writeText( "/*!\r\n" ); 398 399 if( field.pFieldDocs && field.pFieldDocs[ 0 ] ) 400 { 401 stream.writeText( "@brief " ); 402 403 String docs( field.pFieldDocs ); 404 S32 newline = docs.find( '\n' ); 405 if( newline == -1 ) 406 stream.writeText( field.pFieldDocs ); 407 else 408 { 409 String brief = docs.substr( 0, newline ); 410 String body = docs.substr( newline + 1 ); 411 412 stream.writeText( brief ); 413 stream.writeText( "\r\n\r\n" ); 414 stream.writeText( body ); 415 } 416 417 stream.writeText( "\r\n" ); 418 } 419 420 const bool isDeprecated = ( field.type == AbstractClassRep::DeprecatedFieldType ); 421 if( isDeprecated ) 422 stream.writeText( "@deprecated This member is deprecated and its value is always undefined.\r\n" ); 423 424 stream.writeText( "*/\r\n" ); 425 426 ConsoleBaseType* cbt = ConsoleBaseType::getType( field.type ); 427 const char* type = ( cbt ? cbt->getTypeClassName() : "" ); 428 429 if( field.elementCount > 1 ) 430 stream.writeText( String::ToString( "%s %s[ %i ];\r\n", isDeprecated ? "deprecated" : type, field.pFieldname, field.elementCount ) ); 431 else 432 stream.writeText( String::ToString( "%s %s;\r\n", isDeprecated ? "deprecated" : type, field.pFieldname ) ); 433} 434 435static void dumpClassFooter( Stream &stream ) 436{ 437 stream.writeText( "};\r\n\r\n" ); 438} 439 440static void dumpGroupStart( Stream &stream, 441 const char *aName, 442 const char *aDocs = NULL ) 443{ 444 stream.writeText( String::ToString( "\r\n/*! @name %s\r\n", aName ) ); 445 446 if ( aDocs ) 447 { 448 stream.writeText( aDocs ); 449 stream.writeText( "\r\n" ); 450 } 451 452 stream.writeText( "@{ */\r\n" ); 453 454 // Add a blank comment in order to make sure groups are parsed properly. 455 //Con::printf(" /*! */"); 456} 457 458static void dumpGroupEnd( Stream &stream ) 459{ 460 stream.writeText( "/// @}\r\n\r\n" ); 461} 462 463static void dumpClasses( Stream &stream ) 464{ 465 Namespace::trashCache(); 466 467 VectorPtr<Namespace*> vec; 468 vec.reserve( 1024 ); 469 470 // We use mHashSequence to mark if we have traversed... 471 // so mark all as zero to start. 472 for ( Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext ) 473 walk->mHashSequence = 0; 474 475 for(Namespace *walk = Namespace::mNamespaceList; walk; walk = walk->mNext) 476 { 477 VectorPtr<Namespace*> stack; 478 stack.reserve( 1024 ); 479 480 // Get all the parents of this namespace... (and mark them as we go) 481 Namespace *parentWalk = walk; 482 while(parentWalk) 483 { 484 if(parentWalk->mHashSequence != 0) 485 break; 486 if(parentWalk->mPackage == 0) 487 { 488 parentWalk->mHashSequence = 1; // Mark as traversed. 489 stack.push_back(parentWalk); 490 } 491 parentWalk = parentWalk->mParent; 492 } 493 494 // Load stack into our results vector. 495 while(stack.size()) 496 { 497 vec.push_back(stack[stack.size() - 1]); 498 stack.pop_back(); 499 } 500 } 501 502 // Go through previously discovered classes 503 U32 i; 504 for(i = 0; i < vec.size(); i++) 505 { 506 const char *className = vec[i]->mName; 507 const char *superClassName = vec[i]->mParent ? vec[i]->mParent->mName : NULL; 508 509 // Skip the global namespace, that gets dealt with in dumpFunctions 510 if(!className) 511 continue; 512 513 // We're just dumping engine functions, then we don't want to dump 514 // a class that only contains script functions. So, we iterate over 515 // all the functions. 516 bool found = false; 517 for( Namespace::Entry *ewalk = vec[i]->mEntryList; ewalk; ewalk = ewalk->mNext ) 518 { 519 if( ewalk->mType != Namespace::Entry::ConsoleFunctionType ) 520 { 521 found = true; 522 break; 523 } 524 } 525 526 // If we don't have engine functions and the namespace name 527 // doesn't match the class name... then its a script class. 528 if ( !found && !vec[i]->isClass() ) 529 continue; 530 531 // If we hit a class with no members and no classRep, do clever filtering. 532 if(vec[i]->mEntryList == NULL && vec[i]->mClassRep == NULL) 533 { 534 // Print out a short stub so we get a proper class hierarchy. 535 if ( superClassName ) 536 { 537 // Filter hack; we don't want non-inheriting classes... 538 dumpClassHeader( stream, NULL, className, superClassName ); 539 dumpClassFooter( stream ); 540 } 541 continue; 542 } 543 544 // Skip over hidden or internal classes. 545 if( vec[i]->mUsage && 546 ( dStrstr( vec[i]->mUsage, "@hide" ) || dStrstr( vec[i]->mUsage, "@internal" ) ) ) 547 continue; 548 549 // Print the header for the class.. 550 dumpClassHeader( stream, vec[i]->mUsage, className, superClassName ); 551 552 // Dump all fragments for this class. 553 554 for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; fragment != NULL; fragment = fragment->mNext ) 555 if( fragment->mClass && dStricmp( fragment->mClass, className ) == 0 ) 556 dumpFragment( stream, fragment ); 557 558 // Dump member functions. 559 dumpNamespaceEntries( stream, vec[ i ], false ); 560 561 // Dump callbacks. 562 dumpGroupStart( stream, "Callbacks" ); 563 dumpNamespaceEntries( stream, vec[ i ], true ); 564 dumpGroupEnd( stream ); 565 566 // Dump static member variables. 567 dumpVariables( stream, className ); 568 569 // Deal with the classRep (to get members)... 570 AbstractClassRep *rep = vec[i]->mClassRep; 571 AbstractClassRep::FieldList emptyList; 572 AbstractClassRep::FieldList *parentList = &emptyList; 573 AbstractClassRep::FieldList *fieldList = &emptyList; 574 if ( rep ) 575 { 576 // Get information about the parent's fields... 577 AbstractClassRep *parentRep = vec[i]->mParent ? vec[i]->mParent->mClassRep : NULL; 578 if(parentRep) 579 parentList = &(parentRep->mFieldList); 580 581 // Get information about our fields 582 fieldList = &(rep->mFieldList); 583 584 // Go through all our fields... 585 for(U32 j = 0; j < fieldList->size(); j++) 586 { 587 const AbstractClassRep::Field &field = (*fieldList)[j]; 588 589 switch( field.type ) 590 { 591 case AbstractClassRep::StartArrayFieldType: 592 case AbstractClassRep::EndArrayFieldType: 593 break; 594 case AbstractClassRep::StartGroupFieldType: 595 dumpGroupStart( stream, field.pGroupname, field.pFieldDocs ); 596 break; 597 case AbstractClassRep::EndGroupFieldType: 598 dumpGroupEnd( stream ); 599 break; 600 default: 601 case AbstractClassRep::DeprecatedFieldType: 602 // Skip over fields that are already defined in 603 // our parent class. 604 if ( parentRep && parentRep->findField( field.pFieldname ) ) 605 continue; 606 607 dumpClassMember( stream, field ); 608 break; 609 } 610 } 611 } 612 613 // Close the class/namespace. 614 dumpClassFooter( stream ); 615 } 616} 617 618static void dumpEnum( Stream& stream, const EngineTypeInfo* type ) 619{ 620 if( !type->getEnumTable() ) // Sanity check. 621 return; 622 623 // Skip internals... don't export them. 624 if ( type->getDocString() && 625 ( dStrstr( type->getDocString(), "@hide" ) || dStrstr( type->getDocString(), "@internal" ) ) ) 626 return; 627 628 // Write documentation. 629 630 stream.writeText( "/*!\r\n" ); 631 dumpDoc( stream, type->getDocString() ); 632 stream.writeText( "*/\r\n" ); 633 634 // Write definition. 635 636 stream.writeText( "enum " ); 637 stream.writeText( type->getTypeName() ); 638 stream.writeText( " {\r\n" ); 639 640 const EngineEnumTable& table = *( type->getEnumTable() ); 641 const U32 numValues = table.getNumValues(); 642 643 for( U32 i = 0; i < numValues; ++ i ) 644 { 645 const EngineEnumTable::Value& value = table[ i ]; 646 647 stream.writeText( "/*!\r\n" ); 648 dumpDoc( stream, value.getDocString(), false ); 649 stream.writeText( "*/\r\n" ); 650 stream.writeText( value.getName() ); 651 stream.writeText( ",\r\n" ); 652 } 653 654 stream.writeText( "};\r\n" ); 655} 656 657static void dumpEnums( Stream& stream ) 658{ 659 for( const EngineTypeInfo* type = EngineTypeInfo::getFirstType(); 660 type != NULL; type = type->getNextType() ) 661 if( type->isEnum() || type->isBitfield() ) 662 dumpEnum( stream, type ); 663} 664 665static bool dumpEngineDocs( const char *outputFile ) 666{ 667 // Create the output stream. 668 FileStream stream; 669 if ( !stream.open( outputFile, Torque::FS::File::Write ) ) 670 { 671 Con::errorf( "dumpEngineDocs - Failed to open output file." ); 672 return false; 673 } 674 675 // First dump all global ConsoleDoc fragments. 676 677 for( ConsoleDocFragment* fragment = ConsoleDocFragment::smFirst; fragment != NULL; fragment = fragment->mNext ) 678 if( !fragment->mClass ) 679 dumpFragment( stream, fragment ); 680 681 // Clear the doc groups before continuing, 682 smDocGroups.clear(); 683 684 // Dump enumeration types. 685 dumpEnums( stream ); 686 687 // Dump all global variables. 688 dumpVariables( stream ); 689 690 // Now dump the global functions. 691 Namespace *g = Namespace::find( NULL ); 692 while( g ) 693 { 694 dumpNamespaceEntries( stream, g ); 695 696 // Dump callbacks. 697 dumpGroupStart( stream, "Callbacks" ); 698 dumpNamespaceEntries( stream, g, true ); 699 dumpGroupEnd( stream ); 700 701 g = g->mParent; 702 } 703 704 // Now dump all the classes. 705 dumpClasses( stream ); 706 707 // Dump pre-declarations for any groups we encountered 708 // so that we don't have to explicitly define them. 709 HashTable<String,U32>::Iterator iter = smDocGroups.begin(); 710 for (; iter != smDocGroups.end(); ++iter) 711 stream.writeText( String::ToString( "/*! @addtogroup %s */\r\n\r\n", iter->key.c_str() ) ); 712 713 return true; 714} 715 716DefineEngineFunction( dumpEngineDocs, bool, ( const char* outputFile ),, 717 "Dumps the engine scripting documentation to the specified file overwriting any existing content.\n" 718 "@param outputFile The relative or absolute output file path and name.\n" 719 "@return Returns true if successful.\n" 720 "@ingroup Console") 721{ 722 return dumpEngineDocs( outputFile ); 723} 724 725