/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include
#include
#include "Generator.h"
#include "Specification.h"
#include "Utilities.h"
using namespace std;
struct DetailedFunctionEntry {
VersionInfo info;
string htmlDeclaration;
};
static const char OVERVIEW_HTML_FILE_NAME[] = "overview.html";
static const char OVERVIEW_JD_FILE_NAME[] = "overview.jd";
static const char INDEX_HTML_FILE_NAME[] = "index.html";
static const char INDEX_JD_FILE_NAME[] = "index.jd";
static void writeHeader(GeneratedFile* file, bool forVerification, const string& title) {
if (forVerification) {
*file << "\n";
*file << "\n";
*file << "\n"
"RenderScript Reference\n"
"\n"
"\n"
"\n"
"\n"
"\n"
"\n\n";
*file << "
" << title << "
\n";
} else {
*file << "page.title=RenderScript " << title << "\n\n";
*file << "@jd:body\n\n";
}
}
static void writeFooter(GeneratedFile* file, bool forVerification) {
if (forVerification) {
*file << "\n";
}
}
// If prefix starts input, copy it to stream and remove it from input.
static void skipPrefix(ostringstream* stream, string* input, const string& prefix) {
size_t size = prefix.size();
if (input->compare(0, size, prefix) != 0) {
return;
}
input->erase(0, size);
*stream << prefix;
}
// Merge b into a. Returns true if successful
static bool mergeVersionInfo(VersionInfo* a, const VersionInfo& b) {
if (a->intSize != b.intSize) {
cerr << "Error. We don't currently support versions that differ based on int size\n";
return false;
}
if (b.minVersion != 0 && a->maxVersion == b.minVersion - 1) {
a->maxVersion = b.maxVersion;
} else if (b.maxVersion != 0 && a->minVersion == b.maxVersion + 1) {
a->minVersion = b.minVersion;
} else {
cerr << "Error. This code currently assume that all versions are contiguous. Don't know "
"how to merge versions (" << a->minVersion << " - " << a->maxVersion << ") and ("
<< b.minVersion << " - " << b.maxVersion << ")\n";
return false;
}
return true;
}
static string getHtmlStringForType(const ParameterDefinition& parameter) {
string s = parameter.rsType;
ostringstream stream;
skipPrefix(&stream, &s, "const ");
skipPrefix(&stream, &s, "volatile ");
bool endsWithAsterisk = s.size() > 0 && s[s.size() - 1] == '*';
if (endsWithAsterisk) {
s.erase(s.size() - 1, 1);
}
string anchor = systemSpecification.getHtmlAnchor(s);
if (anchor.empty()) {
// Not a RenderScript specific type.
return parameter.rsType;
} else {
stream << anchor;
}
if (endsWithAsterisk) {
stream << "*";
}
return stream.str();
}
static string getDetailedHtmlDeclaration(const FunctionPermutation& permutation) {
ostringstream stream;
auto ret = permutation.getReturn();
if (ret) {
stream << getHtmlStringForType(*ret);
} else {
stream << "void";
}
stream << " " << permutation.getName() << "(";
bool needComma = false;
for (auto p : permutation.getParams()) {
if (needComma) {
stream << ", ";
}
stream << getHtmlStringForType(*p);
if (p->isOutParameter) {
stream << "*";
}
if (!p->specName.empty()) {
stream << " " << p->specName;
}
needComma = true;
}
stream << ");\n";
return stream.str();
}
/* Some functions (like max) have changed implementations but not their
* declaration. We need to unify these so that we don't end up with entries
* like:
* char max(char a, char b); Removed from API level 20
* char max(char a, char b); Added to API level 20
*/
static bool getUnifiedFunctionPrototypes(Function* function,
map* entries) {
for (auto f : function->getSpecifications()) {
DetailedFunctionEntry entry;
entry.info = f->getVersionInfo();
for (auto p : f->getPermutations()) {
entry.htmlDeclaration = getDetailedHtmlDeclaration(*p);
const string s = stripHtml(entry.htmlDeclaration);
auto i = entries->find(s);
if (i == entries->end()) {
entries->insert(pair(s, entry));
} else {
if (!mergeVersionInfo(&i->second.info, entry.info)) {
return false;
}
}
}
}
return true;
}
// Convert words starting with @ into HTML references. Returns false if error.
static bool convertDocumentationRefences(string* s) {
bool success = true;
size_t end = 0;
for (;;) {
size_t start = s->find('@', end);
if (start == string::npos) {
break;
}
// Find the end of the identifier
end = start;
char c;
do {
c = (*s)[++end];
} while (isalnum(c) || c == '_');
const string id = s->substr(start + 1, end - start - 1);
string anchor = systemSpecification.getHtmlAnchor(id);
if (anchor.empty()) {
cerr << "Error: Can't convert the documentation reference @" << id << "\n";
success = false;
}
s->replace(start, end - start, anchor);
}
return success;
}
static bool generateHtmlParagraphs(GeneratedFile* file, const vector& description) {
bool inParagraph = false;
for (auto s : description) {
// Empty lines in the .spec marks paragraphs.
if (s.empty()) {
if (inParagraph) {
*file << "
\n";
if (!generateHtmlParagraphs(&file, specFile.getFullDescription())) {
success = false;
}
// Write the summary tables.
file << "
Summary
\n";
const auto& constants = specFile.getDocumentedConstants();
const auto& types = specFile.getDocumentedTypes();
const auto& functions = specFile.getDocumentedFunctions();
writeSummaryTables(&file, constants, types, functions, NON_DEPRECATED_ONLY, false);
writeSummaryTables(&file, constants, types, functions, DEPRECATED_ONLY, false);
// Write the full details of each constant, type, and function.
if (!constants.empty()) {
file << "
Constants
\n";
for (auto i : constants) {
if (!writeDetailedConstant(&file, i.second)) {
success = false;
}
}
}
if (!types.empty()) {
file << "
Types
\n";
for (auto i : types) {
if (!writeDetailedType(&file, i.second)) {
success = false;
}
}
}
if (!functions.empty()) {
file << "
Functions
\n";
for (auto i : functions) {
if (!writeDetailedFunction(&file, i.second)) {
success = false;
}
}
}
writeFooter(&file, forVerification);
file.close();
if (!success) {
// If in error, write a final message to make it easier to figure out which file failed.
cerr << fileName << ": Failed due to errors.\n";
}
return success;
}
static void generateSnippet(GeneratedFile* file, const string& fileName, const string& title) {
const char offset[] = " ";
*file << offset << "
\n";
}
/* Generate a partial file of links that should be cut & pasted into the proper section of the
* guide_toc.cs file.
*/
static bool generateAndroidTableOfContentSnippet(const string& directory) {
GeneratedFile file;
if (!file.start(directory, "guide_toc.cs")) {
return false;
}
file << "