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