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 "Utilities.h"
22
23using namespace std;
24
25const char LEGAL_NOTICE[] =
26            "/*\n"
27            " * Copyright (C) 2016 The Android Open Source Project\n"
28            " *\n"
29            " * Licensed under the Apache License, Version 2.0 (the \"License\");\n"
30            " * you may not use this file except in compliance with the License.\n"
31            " * You may obtain a copy of the License at\n"
32            " *\n"
33            " *      http://www.apache.org/licenses/LICENSE-2.0\n"
34            " *\n"
35            " * Unless required by applicable law or agreed to in writing, software\n"
36            " * distributed under the License is distributed on an \"AS IS\" BASIS,\n"
37            " * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
38            " * See the License for the specific language governing permissions and\n"
39            " * limitations under the License.\n"
40            " */\n\n";
41
42const char AUTO_GENERATED_WARNING[] =
43            "Don't edit this file!  It is auto-generated by frameworks/rs/api/generate.sh.";
44
45string capitalize(const string& source) {
46    int length = source.length();
47    string result;
48    bool capitalize = true;
49    for (int s = 0; s < length; s++) {
50        if (source[s] == '_') {
51            capitalize = true;
52        } else if (capitalize) {
53            result += toupper(source[s]);
54            capitalize = false;
55        } else {
56            result += source[s];
57        }
58    }
59    return result;
60}
61
62void trimSpaces(string* s) {
63    const size_t end = s->find_last_not_of(" ");
64    if (end == string::npos) {
65        // All spaces
66        s->erase();
67        return;
68    } else {
69        s->erase(end + 1);
70    }
71    const size_t start = s->find_first_not_of(" ");
72    if (start > 0) {
73        s->erase(0, start);
74    }
75}
76
77string stringReplace(string s, string match, string rep) {
78    while (1) {
79        // This is not efficient but we don't care, as this program runs very rarely.
80        size_t p = s.find(match);
81        if (p == string::npos) break;
82
83        s.erase(p, match.size());
84        s.insert(p, rep);
85    }
86    return s;
87}
88
89bool charRemoved(char c, string* s) {
90    size_t p = s->find(c);
91    if (p != string::npos) {
92        s->erase(p, 1);
93        return true;
94    }
95    return false;
96}
97
98string stripHtml(const string& html) {
99    string in = stringReplace(html, "<li>", "- ");
100    string out;
101    for (size_t start = 0; start < in.size(); start++) {
102        size_t lt = in.find('<', start);
103        if (lt == string::npos) {
104            out += in.substr(start);
105            break;
106        }
107        out += in.substr(start, lt - start);
108        if (isalpha(in[lt + 1]) || in[lt + 1] == '/') {
109            // It's an HTML tag.  Search for the end.
110            start = in.find('>', lt + 1);
111            if (start == string::npos) {
112                break;
113            }
114        } else {
115            out += '<';
116        }
117    }
118    out = stringReplace(out, "&gt;", ">");
119    out = stringReplace(out, "&lt;", "<");
120    out = stringReplace(out, "&nbsp;", " ");
121    out = stringReplace(out, "&amp;", "&");
122    return out;
123}
124
125string hashString(const string& s) {
126    long hash = 0;
127    for (size_t i = 0; i < s.length(); i++) {
128        hash = hash * 43 + s[i];
129    }
130    stringstream stream;
131    stream << "0x" << std::hex << hash << "l";
132    return stream.str();
133}
134
135bool testAndSet(const string& flag, set<string>* set) {
136    if (set->find(flag) == set->end()) {
137        set->insert(flag);
138        return false;
139    }
140    return true;
141}
142
143double maxDoubleForInteger(int numberOfIntegerBits, int mantissaSize) {
144    /* Double has only 52 bits of precision (53 implied). So for longs, we want
145     * to create smaller values to avoid a round up.  Same for floats and halfs.
146     */
147    int lowZeroBits = max(0, numberOfIntegerBits - mantissaSize);
148    uint64_t l = (0xffffffffffffffff >> (64 - numberOfIntegerBits + lowZeroBits))
149                 << lowZeroBits;
150    return (double)l;
151}
152
153// Add the value to the stream, prefixed with a ", " if needed.
154static void addCommaSeparated(const string& value, ostringstream* stream, bool* needComma) {
155    if (value.empty()) {
156        return;
157    }
158    if (*needComma) {
159        *stream << ", ";
160    }
161    *stream << value;
162    *needComma = true;
163}
164
165string makeAttributeTag(const string& userAttribute, const string& additionalAttribute,
166                        unsigned int deprecatedApiLevel, const string& deprecatedMessage) {
167    ostringstream stream;
168    bool needComma = false;
169    if (userAttribute[0] == '=') {
170        /* If starts with an equal, we don't automatically add additionalAttribute.
171         * This is because of the error we made defining rsUnpackColor8888().
172         */
173        addCommaSeparated(userAttribute.substr(1), &stream, &needComma);
174    } else {
175        addCommaSeparated(userAttribute, &stream, &needComma);
176        addCommaSeparated(additionalAttribute, &stream, &needComma);
177    }
178    if (deprecatedApiLevel > 0) {
179        stream << "\n#if (defined(RS_VERSION) && (RS_VERSION >= " << deprecatedApiLevel << "))\n";
180        addCommaSeparated("deprecated", &stream, &needComma);
181        if (!deprecatedMessage.empty()) {
182            // Remove any @ that's used for generating documentation cross references.
183            string s = deprecatedMessage;
184            s.erase(std::remove(s.begin(), s.end(), '@'), s.end());
185            stream << "(\"" << s << "\")";
186        }
187        stream << "\n#endif\n";
188    }
189    if (stream.tellp() == 0) {
190        return "";
191    }
192    return " __attribute__((" + stream.str() + "))";
193}
194
195// Opens the stream.  Reports an error if it can't.
196bool GeneratedFile::start(const string& directory, const string& name) {
197    const string path = directory + "/" + name;
198    open(path.c_str(), ios::out | ios::trunc);
199    if (!is_open()) {
200        cerr << "Error.  Can't open the output file: " << path << "\n";
201        return false;
202    }
203    return true;
204}
205
206void GeneratedFile::writeNotices() {
207    *this << LEGAL_NOTICE;
208    *this << "// " << AUTO_GENERATED_WARNING << "\n\n";
209}
210
211void GeneratedFile::increaseIndent() {
212    mIndent.append(string(TAB_SIZE, ' '));
213}
214
215void GeneratedFile::decreaseIndent() {
216    mIndent.erase(0, TAB_SIZE);
217}
218