1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <algorithm> 18 #include <iostream> 19 #include <sstream> 20 21 #include "Generator.h" 22 #include "Specification.h" 23 #include "Utilities.h" 24 25 using namespace std; 26 27 struct DetailedFunctionEntry { 28 VersionInfo info; 29 string htmlDeclaration; 30 }; 31 32 static const char OVERVIEW_HTML_FILE_NAME[] = "overview.html"; 33 static const char INDEX_HTML_FILE_NAME[] = "index.html"; 34 35 static void writeHeader(GeneratedFile* file, const string& title, 36 const SpecFile& specFile) { 37 // Generate DevSite markups 38 *file 39 << "<html devsite>\n" 40 "<!-- " << AUTO_GENERATED_WARNING << "-->\n" 41 "<head>\n" 42 " <title>RenderScript " << title << "</title>\n" 43 " <meta name=\"top_category\" value=\"develop\" />\n" 44 " <meta name=\"subcategory\" value=\"guide\" />\n" 45 " <meta name=\"book_path\" value=\"/guide/_book.yaml\" />\n" 46 " <meta name=\"project_path\" value=\"/guide/_project.yaml\" />\n"; 47 auto desc = specFile.getFullDescription(); 48 if (desc.size()) { 49 *file << " <meta name=\"description\" content=\""; 50 // Output only the first two lines. Assuming there's no other HTML 51 // markups there 52 // TODO: escape/remove markups 53 for (unsigned int i = 0; i < std::min(desc.size(), 2UL); ++i) { 54 if (i) *file << " "; 55 *file << desc[i]; 56 } 57 *file << "\">\n"; 58 } 59 *file << "</head>\n\n" 60 "<body>\n\n"; 61 *file << "<div class='renderscript'>\n"; 62 } 63 64 static void writeFooter(GeneratedFile* file) { 65 *file << "</div>\n"; 66 *file << "\n\n</body>\n"; 67 *file << "</html>\n"; 68 } 69 70 // If prefix starts input, copy it to stream and remove it from input. 71 static void skipPrefix(ostringstream* stream, string* input, const string& prefix) { 72 size_t size = prefix.size(); 73 if (input->compare(0, size, prefix) != 0) { 74 return; 75 } 76 input->erase(0, size); 77 *stream << prefix; 78 } 79 80 // Merge b into a. Returns true if successful 81 static bool mergeVersionInfo(VersionInfo* a, const VersionInfo& b) { 82 if (a->intSize != b.intSize) { 83 cerr << "Error. We don't currently support versions that differ based on int size\n"; 84 return false; 85 } 86 if (b.minVersion != 0 && a->maxVersion == b.minVersion - 1) { 87 a->maxVersion = b.maxVersion; 88 } else if (b.maxVersion != 0 && a->minVersion == b.maxVersion + 1) { 89 a->minVersion = b.minVersion; 90 } else { 91 cerr << "Error. This code currently assume that all versions are contiguous. Don't know " 92 "how to merge versions (" << a->minVersion << " - " << a->maxVersion << ") and (" 93 << b.minVersion << " - " << b.maxVersion << ")\n"; 94 return false; 95 } 96 return true; 97 } 98 99 static string getHtmlStringForType(const ParameterDefinition& parameter) { 100 string s = parameter.rsType; 101 ostringstream stream; 102 skipPrefix(&stream, &s, "const "); 103 skipPrefix(&stream, &s, "volatile "); 104 bool endsWithAsterisk = s.size() > 0 && s[s.size() - 1] == '*'; 105 if (endsWithAsterisk) { 106 s.erase(s.size() - 1, 1); 107 } 108 109 string anchor = systemSpecification.getHtmlAnchor(s); 110 if (anchor.empty()) { 111 // Not a RenderScript specific type. 112 return parameter.rsType; 113 } else { 114 stream << anchor; 115 } 116 if (endsWithAsterisk) { 117 stream << "*"; 118 } 119 return stream.str(); 120 } 121 122 static string getDetailedHtmlDeclaration(const FunctionPermutation& permutation) { 123 ostringstream stream; 124 auto ret = permutation.getReturn(); 125 if (ret) { 126 stream << getHtmlStringForType(*ret); 127 } else { 128 stream << "void"; 129 } 130 stream << " " << permutation.getName() << "("; 131 bool needComma = false; 132 for (auto p : permutation.getParams()) { 133 if (needComma) { 134 stream << ", "; 135 } 136 stream << getHtmlStringForType(*p); 137 if (p->isOutParameter) { 138 stream << "*"; 139 } 140 if (!p->specName.empty()) { 141 stream << " " << p->specName; 142 } 143 needComma = true; 144 } 145 stream << ");\n"; 146 return stream.str(); 147 } 148 149 /* Some functions (like max) have changed implementations but not their 150 * declaration. We need to unify these so that we don't end up with entries 151 * like: 152 * char max(char a, char b); Removed from API level 20 153 * char max(char a, char b); Added to API level 20 154 */ 155 static bool getUnifiedFunctionPrototypes(Function* function, 156 map<string, DetailedFunctionEntry>* entries) { 157 for (auto f : function->getSpecifications()) { 158 DetailedFunctionEntry entry; 159 entry.info = f->getVersionInfo(); 160 for (auto p : f->getPermutations()) { 161 entry.htmlDeclaration = getDetailedHtmlDeclaration(*p); 162 const string s = stripHtml(entry.htmlDeclaration); 163 auto i = entries->find(s); 164 if (i == entries->end()) { 165 entries->insert(pair<string, DetailedFunctionEntry>(s, entry)); 166 } else { 167 if (!mergeVersionInfo(&i->second.info, entry.info)) { 168 return false; 169 } 170 } 171 } 172 } 173 return true; 174 } 175 176 // Convert words starting with @ into HTML references. Returns false if error. 177 static bool convertDocumentationRefences(string* s) { 178 bool success = true; 179 size_t end = 0; 180 for (;;) { 181 size_t start = s->find('@', end); 182 if (start == string::npos) { 183 break; 184 } 185 // Find the end of the identifier 186 end = start; 187 char c; 188 do { 189 c = (*s)[++end]; 190 } while (isalnum(c) || c == '_'); 191 192 const string id = s->substr(start + 1, end - start - 1); 193 string anchor = systemSpecification.getHtmlAnchor(id); 194 if (anchor.empty()) { 195 cerr << "Error: Can't convert the documentation reference @" << id << "\n"; 196 success = false; 197 } 198 s->replace(start, end - start, anchor); 199 } 200 return success; 201 } 202 203 static bool generateHtmlParagraphs(GeneratedFile* file, const vector<string>& description) { 204 bool inParagraph = false; 205 for (auto s : description) { 206 // Empty lines in the .spec marks paragraphs. 207 if (s.empty()) { 208 if (inParagraph) { 209 *file << "</p>\n"; 210 inParagraph = false; 211 } 212 } else { 213 if (!inParagraph) { 214 *file << "<p> "; 215 inParagraph = true; 216 } 217 } 218 if (!convertDocumentationRefences(&s)) { 219 return false; 220 } 221 *file << s << "\n"; 222 } 223 if (inParagraph) { 224 *file << "</p>\n"; 225 } 226 return true; 227 } 228 229 static void writeSummaryTableStart(GeneratedFile* file, const string& label, bool labelIsHeading) { 230 if (labelIsHeading) { 231 *file << "<h2 style='margin-bottom: 0px;'>" << label << "</h2>\n"; 232 } 233 *file << "<table class='jd-sumtable'><tbody>\n"; 234 if (!labelIsHeading) { 235 *file << " <tr><th colspan='2'>" << label << "</th></tr>\n"; 236 } 237 } 238 239 static void writeSummaryTableEnd(GeneratedFile* file) { 240 *file << "</tbody></table>\n"; 241 } 242 243 enum DeprecatedSelector { 244 DEPRECATED_ONLY, 245 NON_DEPRECATED_ONLY, 246 ALL, 247 }; 248 249 static void writeSummaryTableEntry(ostream* stream, Definition* definition, 250 DeprecatedSelector deprecatedSelector) { 251 if (definition->hidden()) { 252 return; 253 } 254 const bool deprecated = definition->deprecated(); 255 if ((deprecatedSelector == DEPRECATED_ONLY && !deprecated) || 256 (deprecatedSelector == NON_DEPRECATED_ONLY && deprecated)) { 257 return; 258 } 259 260 *stream << " <tr class='alt-color api apilevel-1'>\n"; 261 *stream << " <td class='jd-linkcol'>\n"; 262 *stream << " <a href='" << definition->getUrl() << "'>" << definition->getName() 263 << "</a>\n"; 264 *stream << " </td>\n"; 265 *stream << " <td class='jd-descrcol' width='100%'>\n"; 266 *stream << " "; 267 if (deprecated) { 268 *stream << "<b>Deprecated</b>. "; 269 } 270 *stream << definition->getSummary() << "\n"; 271 *stream << " </td>\n"; 272 *stream << " </tr>\n"; 273 } 274 275 static void writeSummaryTable(GeneratedFile* file, const ostringstream* entries, const char* name, 276 DeprecatedSelector deprecatedSelector, bool labelAsHeader) { 277 string s = entries->str(); 278 if (!s.empty()) { 279 string prefix; 280 if (deprecatedSelector == DEPRECATED_ONLY) { 281 prefix = "Deprecated "; 282 } 283 writeSummaryTableStart(file, prefix + name, labelAsHeader); 284 *file << s; 285 writeSummaryTableEnd(file); 286 } 287 } 288 289 static void writeSummaryTables(GeneratedFile* file, const map<string, Constant*>& constants, 290 const map<string, Type*>& types, 291 const map<string, Function*>& functions, 292 DeprecatedSelector deprecatedSelector, bool labelAsHeader) { 293 ostringstream constantStream; 294 for (auto e : constants) { 295 writeSummaryTableEntry(&constantStream, e.second, deprecatedSelector); 296 } 297 writeSummaryTable(file, &constantStream, "Constants", deprecatedSelector, labelAsHeader); 298 299 ostringstream typeStream; 300 for (auto e : types) { 301 writeSummaryTableEntry(&typeStream, e.second, deprecatedSelector); 302 } 303 writeSummaryTable(file, &typeStream, "Types", deprecatedSelector, labelAsHeader); 304 305 ostringstream functionStream; 306 for (auto e : functions) { 307 writeSummaryTableEntry(&functionStream, e.second, deprecatedSelector); 308 } 309 writeSummaryTable(file, &functionStream, "Functions", deprecatedSelector, labelAsHeader); 310 } 311 312 static void writeHtmlVersionTag(GeneratedFile* file, VersionInfo info, 313 bool addSpacing) { 314 ostringstream stream; 315 if (info.intSize == 32) { 316 stream << "When compiling for 32 bits. "; 317 } else if (info.intSize == 64) { 318 stream << "When compiling for 64 bits. "; 319 } 320 321 if (info.minVersion > 1 || info.maxVersion) { 322 const char* mid = 323 "<a " 324 "href='http://developer.android.com/guide/topics/manifest/" 325 "uses-sdk-element.html#ApiLevels'>API level "; 326 if (info.minVersion <= 1) { 327 // No minimum 328 if (info.maxVersion > 0) { 329 stream << "Removed from " << mid << info.maxVersion + 1 << " and higher"; 330 } 331 } else { 332 if (info.maxVersion == 0) { 333 // No maximum 334 stream << "Added in " << mid << info.minVersion; 335 } else { 336 stream << mid << info.minVersion << " - " << info.maxVersion; 337 } 338 } 339 stream << "</a>"; 340 } 341 string s = stream.str(); 342 // Remove any trailing whitespace 343 while (s.back() == ' ') { 344 s.pop_back(); 345 } 346 if (!s.empty()) { 347 *file << (addSpacing ? " " : "") << s << "\n"; 348 } 349 } 350 351 static void writeDetailedTypeSpecification(GeneratedFile* file, const TypeSpecification* spec) { 352 switch (spec->getKind()) { 353 case SIMPLE: { 354 Type* type = spec->getType(); 355 *file << "<p>A typedef of: " << spec->getSimpleType() 356 << makeAttributeTag(spec->getAttribute(), "", type->getDeprecatedApiLevel(), 357 type->getDeprecatedMessage()) 358 << " "; 359 writeHtmlVersionTag(file, spec->getVersionInfo(), false); 360 *file << "</p>\n"; 361 break; 362 } 363 case RS_OBJECT: { 364 *file << "<p>"; 365 writeHtmlVersionTag(file, spec->getVersionInfo(), false); 366 *file << "</p>\n"; 367 break; 368 } 369 case ENUM: { 370 *file << "<p>An enum with the following values: \n"; 371 writeHtmlVersionTag(file, spec->getVersionInfo(), false); 372 *file << "</p>\n"; 373 374 *file << " <table class='jd-tagtable'><tbody>\n"; 375 const vector<string>& values = spec->getValues(); 376 const vector<string>& valueComments = spec->getValueComments(); 377 for (size_t i = 0; i < values.size(); i++) { 378 *file << " <tr><th>" << values[i] << "</th><td>"; 379 if (valueComments.size() > i) { 380 *file << valueComments[i]; 381 } 382 *file << "</td></tr>\n"; 383 } 384 *file << " </tbody></table><br/>\n"; 385 break; 386 } 387 case STRUCT: { 388 *file << "<p>A structure with the following fields: "; 389 writeHtmlVersionTag(file, spec->getVersionInfo(), false); 390 *file << "</p>\n"; 391 392 *file << " <table class='jd-tagtable'><tbody>\n"; 393 const vector<string>& fields = spec->getFields(); 394 const vector<string>& fieldComments = spec->getFieldComments(); 395 for (size_t i = 0; i < fields.size(); i++) { 396 *file << " <tr><th>" << fields[i] << "</th><td>"; 397 if (fieldComments.size() > i && !fieldComments[i].empty()) { 398 *file << fieldComments[i]; 399 } 400 *file << "</td></tr>\n"; 401 } 402 *file << " </tbody></table><br/>\n"; 403 break; 404 } 405 } 406 } 407 408 static void writeDetailedConstantSpecification(GeneratedFile* file, ConstantSpecification* c) { 409 *file << " <tr><td>"; 410 *file << "Value: " << c->getValue() << "\n"; 411 writeHtmlVersionTag(file, c->getVersionInfo(), true); 412 *file << " </td></tr>\n"; 413 *file << "<br/>\n"; 414 } 415 416 static bool writeOverviewForFile(GeneratedFile* file, const SpecFile& specFile) { 417 bool success = true; 418 *file << "<h2>" << specFile.getBriefDescription() << "</h2>\n"; 419 if (!generateHtmlParagraphs(file, specFile.getFullDescription())) { 420 success = false; 421 } 422 423 // Write the summary tables. 424 // file << "<h2>Summary</h2>\n"; 425 writeSummaryTables(file, specFile.getDocumentedConstants(), specFile.getDocumentedTypes(), 426 specFile.getDocumentedFunctions(), NON_DEPRECATED_ONLY, false); 427 428 return success; 429 } 430 431 static bool generateOverview(const string& directory) { 432 GeneratedFile file; 433 if (!file.start(directory, OVERVIEW_HTML_FILE_NAME)) { 434 return false; 435 } 436 bool success = true; 437 438 // Take the description from the first spec file (rs_core.spec, based on how 439 // currently this generator is called) 440 writeHeader(&file, "Runtime API Reference", 441 *(systemSpecification.getSpecFiles()[0])); 442 443 for (auto specFile : systemSpecification.getSpecFiles()) { 444 if (!writeOverviewForFile(&file, *specFile)) { 445 success = false; 446 } 447 } 448 449 writeFooter(&file); 450 file.close(); 451 return success; 452 } 453 454 static bool generateAlphabeticalIndex(const string& directory) { 455 GeneratedFile file; 456 if (!file.start(directory, INDEX_HTML_FILE_NAME)) { 457 return false; 458 } 459 writeHeader(&file, "Index", SpecFile("")); 460 461 writeSummaryTables(&file, systemSpecification.getConstants(), systemSpecification.getTypes(), 462 systemSpecification.getFunctions(), NON_DEPRECATED_ONLY, true); 463 464 writeSummaryTables(&file, systemSpecification.getConstants(), systemSpecification.getTypes(), 465 systemSpecification.getFunctions(), DEPRECATED_ONLY, true); 466 467 writeFooter(&file); 468 file.close(); 469 return true; 470 } 471 472 static void writeDeprecatedWarning(GeneratedFile* file, Definition* definition) { 473 if (definition->deprecated()) { 474 *file << " <p><b>Deprecated.</b> "; 475 string s = definition->getDeprecatedMessage(); 476 convertDocumentationRefences(&s); 477 if (!s.empty()) { 478 *file << s; 479 } else { 480 *file << "Do not use."; 481 } 482 *file << "</p>\n"; 483 } 484 } 485 486 static bool writeDetailedConstant(GeneratedFile* file, Constant* constant) { 487 if (constant->hidden()) { 488 return true; 489 } 490 const string& name = constant->getName(); 491 492 *file << "<a name='android_rs:" << name << "'></a>\n"; 493 *file << "<div class='jd-details'>\n"; 494 *file << " <h4 class='jd-details-title'>\n"; 495 *file << " <span class='sympad'>" << name << "</span>\n"; 496 *file << " <span class='normal'>: " << constant->getSummary() << "</span>\n"; 497 *file << " </h4>\n"; 498 499 *file << " <div class='jd-details-descr'>\n"; 500 *file << " <table class='jd-tagtable'><tbody>\n"; 501 auto specifications = constant->getSpecifications(); 502 bool addSeparator = specifications.size() > 1; 503 for (auto spec : specifications) { 504 if (addSeparator) { 505 *file << " <h5 class='jd-tagtitle'>Variant:</h5>\n"; 506 } 507 writeDetailedConstantSpecification(file, spec); 508 } 509 *file << " </tbody></table>\n"; 510 *file << " </div>\n"; 511 512 *file << " <div class='jd-tagdata jd-tagdescr'>\n"; 513 514 writeDeprecatedWarning(file, constant); 515 if (!generateHtmlParagraphs(file, constant->getDescription())) { 516 return false; 517 } 518 *file << " </div>\n"; 519 520 *file << "</div>\n"; 521 *file << "\n"; 522 return true; 523 } 524 525 static bool writeDetailedType(GeneratedFile* file, Type* type) { 526 if (type->hidden()) { 527 return true; 528 } 529 const string& name = type->getName(); 530 531 *file << "<a name='android_rs:" << name << "'></a>\n"; 532 *file << "<div class='jd-details'>\n"; 533 *file << " <h4 class='jd-details-title'>\n"; 534 *file << " <span class='sympad'>" << name << "</span>\n"; 535 *file << " <span class='normal'>: " << type->getSummary() << "</span>\n"; 536 *file << " </h4>\n"; 537 538 *file << " <div class='jd-details-descr'>\n"; 539 for (auto spec : type->getSpecifications()) { 540 writeDetailedTypeSpecification(file, spec); 541 } 542 543 writeDeprecatedWarning(file, type); 544 if (!generateHtmlParagraphs(file, type->getDescription())) { 545 return false; 546 } 547 548 *file << " </div>\n"; 549 *file << "</div>\n"; 550 *file << "\n"; 551 return true; 552 } 553 554 static bool writeDetailedFunction(GeneratedFile* file, Function* function) { 555 if (function->hidden()) { 556 return true; 557 } 558 const string& name = function->getName(); 559 560 *file << "<a name='android_rs:" << name << "'></a>\n"; 561 *file << "<div class='jd-details'>\n"; 562 *file << " <h4 class='jd-details-title'>\n"; 563 *file << " <span class='sympad'>" << name << "</span>\n"; 564 *file << " <span class='normal'>: " << function->getSummary() << "</span>\n"; 565 *file << " </h4>\n"; 566 567 *file << " <div class='jd-details-descr'>\n"; 568 map<string, DetailedFunctionEntry> entries; 569 if (!getUnifiedFunctionPrototypes(function, &entries)) { 570 return false; 571 } 572 *file << " <table class='jd-tagtable'><tbody>\n"; 573 for (auto i : entries) { 574 *file << " <tr>\n"; 575 *file << " <td>" << i.second.htmlDeclaration << "</td>\n"; 576 *file << " <td>"; 577 writeHtmlVersionTag(file, i.second.info, true); 578 *file << " </td>\n"; 579 *file << " </tr>\n"; 580 } 581 *file << " </tbody></table>\n"; 582 *file << " </div>\n"; 583 584 if (function->someParametersAreDocumented()) { 585 *file << " <div class='jd-tagdata'>"; 586 *file << " <h5 class='jd-tagtitle'>Parameters</h5>\n"; 587 *file << " <table class='jd-tagtable'><tbody>\n"; 588 for (ParameterEntry* p : function->getParameters()) { 589 *file << " <tr><th>" << p->name << "</th><td>" << p->documentation << "</td></tr>\n"; 590 } 591 *file << " </tbody></table>\n"; 592 *file << " </div>\n"; 593 } 594 595 string ret = function->getReturnDocumentation(); 596 if (!ret.empty()) { 597 *file << " <div class='jd-tagdata'>"; 598 *file << " <h5 class='jd-tagtitle'>Returns</h5>\n"; 599 *file << " <table class='jd-tagtable'><tbody>\n"; 600 *file << " <tr><td>" << ret << "</td></tr>\n"; 601 *file << " </tbody></table>\n"; 602 *file << " </div>\n"; 603 } 604 605 *file << " <div class='jd-tagdata jd-tagdescr'>\n"; 606 writeDeprecatedWarning(file, function); 607 if (!generateHtmlParagraphs(file, function->getDescription())) { 608 return false; 609 } 610 *file << " </div>\n"; 611 612 *file << "</div>\n"; 613 *file << "\n"; 614 return true; 615 } 616 617 static bool writeDetailedDocumentationFile(const string& directory, 618 const SpecFile& specFile) { 619 if (!specFile.hasSpecifications()) { 620 // This is true for rs_core.spec 621 return true; 622 } 623 624 GeneratedFile file; 625 const string fileName = stringReplace(specFile.getSpecFileName(), ".spec", 626 ".html"); 627 if (!file.start(directory, fileName)) { 628 return false; 629 } 630 bool success = true; 631 632 string title = specFile.getBriefDescription(); 633 writeHeader(&file, title, specFile); 634 635 file << "<h2>Overview</h2>\n"; 636 if (!generateHtmlParagraphs(&file, specFile.getFullDescription())) { 637 success = false; 638 } 639 640 // Write the summary tables. 641 file << "<h2>Summary</h2>\n"; 642 const auto& constants = specFile.getDocumentedConstants(); 643 const auto& types = specFile.getDocumentedTypes(); 644 const auto& functions = specFile.getDocumentedFunctions(); 645 646 writeSummaryTables(&file, constants, types, functions, NON_DEPRECATED_ONLY, false); 647 writeSummaryTables(&file, constants, types, functions, DEPRECATED_ONLY, false); 648 649 // Write the full details of each constant, type, and function. 650 if (!constants.empty()) { 651 file << "<h2>Constants</h2>\n"; 652 for (auto i : constants) { 653 if (!writeDetailedConstant(&file, i.second)) { 654 success = false; 655 } 656 } 657 } 658 if (!types.empty()) { 659 file << "<h2>Types</h2>\n"; 660 for (auto i : types) { 661 if (!writeDetailedType(&file, i.second)) { 662 success = false; 663 } 664 } 665 } 666 if (!functions.empty()) { 667 file << "<h2>Functions</h2>\n"; 668 for (auto i : functions) { 669 if (!writeDetailedFunction(&file, i.second)) { 670 success = false; 671 } 672 } 673 } 674 675 writeFooter(&file); 676 file.close(); 677 678 if (!success) { 679 // If in error, write a final message to make it easier to figure out which file failed. 680 cerr << fileName << ": Failed due to errors.\n"; 681 } 682 return success; 683 } 684 685 static void generateSnippet(GeneratedFile* file, const string& fileName, const string& title) { 686 const char offset[] = " "; 687 *file << offset << "<li><a href=\"<?cs var:toroot ?>guide/topics/renderscript/reference/" 688 << fileName << "\">\n"; 689 *file << offset << " <span class=\"en\">" << title << "</span>\n"; 690 *file << offset << "</a></li>\n"; 691 } 692 693 /* Generate a partial file of links that should be cut & pasted into the proper section of the 694 * guide_toc.cs file. 695 */ 696 static bool generateAndroidTableOfContentSnippet(const string& directory) { 697 GeneratedFile file; 698 if (!file.start(directory, "guide_toc.cs")) { 699 return false; 700 } 701 file << "<!-- Copy and paste the following lines into the RenderScript section of\n"; 702 file << " platform/frameworks/base/docs/html/guide/guide_toc.cs\n\n"; 703 704 const char offset[] = " "; 705 file << offset << "<li class=\"nav-section\">\n"; 706 file << offset << " <div class=\"nav-section-header\">\n"; 707 file << offset << " <a href=\"<?cs var:toroot ?>guide/topics/renderscript/reference/" << 708 OVERVIEW_HTML_FILE_NAME << "\">\n"; 709 file << offset << " <span class=\"en\">Runtime API Reference</span>\n"; 710 file << offset << " </a></div>\n"; 711 file << offset << " <ul>\n"; 712 713 for (auto specFile : systemSpecification.getSpecFiles()) { 714 if (specFile->hasSpecifications()) { 715 const string fileName = stringReplace(specFile->getSpecFileName(), ".spec", ".html"); 716 generateSnippet(&file, fileName, specFile->getBriefDescription()); 717 } 718 } 719 generateSnippet(&file, INDEX_HTML_FILE_NAME, "Index"); 720 721 file << offset << " </ul>\n"; 722 file << offset << "</li>\n"; 723 724 return true; 725 } 726 727 bool generateDocumentation(const string& directory) { 728 bool success = generateOverview(directory) && 729 generateAlphabeticalIndex(directory) && 730 generateAndroidTableOfContentSnippet(directory); 731 for (auto specFile : systemSpecification.getSpecFiles()) { 732 if (!writeDetailedDocumentationFile(directory, *specFile)) { 733 success = false; 734 } 735 } 736 return success; 737 } 738