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 
writeHeader(GeneratedFile * file,const string & title,const SpecFile & specFile)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 
writeFooter(GeneratedFile * file)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.
skipPrefix(ostringstream * stream,string * input,const string & prefix)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
mergeVersionInfo(VersionInfo * a,const VersionInfo & b)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 
getHtmlStringForType(const ParameterDefinition & parameter)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 
getDetailedHtmlDeclaration(const FunctionPermutation & permutation)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  */
getUnifiedFunctionPrototypes(Function * function,map<string,DetailedFunctionEntry> * entries)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.
convertDocumentationRefences(string * s)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 
generateHtmlParagraphs(GeneratedFile * file,const vector<string> & description)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 
writeSummaryTableStart(GeneratedFile * file,const string & label,bool labelIsHeading)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 
writeSummaryTableEnd(GeneratedFile * file)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 
writeSummaryTableEntry(ostream * stream,Definition * definition,DeprecatedSelector deprecatedSelector)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 
writeSummaryTable(GeneratedFile * file,const ostringstream * entries,const char * name,DeprecatedSelector deprecatedSelector,bool labelAsHeader)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 
writeSummaryTables(GeneratedFile * file,const map<string,Constant * > & constants,const map<string,Type * > & types,const map<string,Function * > & functions,DeprecatedSelector deprecatedSelector,bool labelAsHeader)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 
writeHtmlVersionTag(GeneratedFile * file,VersionInfo info,bool addSpacing)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 
writeDetailedTypeSpecification(GeneratedFile * file,const TypeSpecification * spec)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                   << "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
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:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\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:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;";
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 
writeDetailedConstantSpecification(GeneratedFile * file,ConstantSpecification * c)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 
writeOverviewForFile(GeneratedFile * file,const SpecFile & specFile)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 
generateOverview(const string & directory)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 
generateAlphabeticalIndex(const string & directory)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 
writeDeprecatedWarning(GeneratedFile * file,Definition * definition)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 
writeDetailedConstant(GeneratedFile * file,Constant * constant)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 
writeDetailedType(GeneratedFile * file,Type * type)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 
writeDetailedFunction(GeneratedFile * file,Function * function)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 
writeDetailedDocumentationFile(const string & directory,const SpecFile & specFile)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 
generateSnippet(GeneratedFile * file,const string & fileName,const string & title)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  */
generateAndroidTableOfContentSnippet(const string & directory)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 
generateDocumentation(const string & directory)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