blob: e2ffe79c764ddbf7c3cee463ff784c694d108e86 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
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 "JavaClassGenerator.h"
Adam Lesinski769de982015-04-10 19:43:55 -070018#include "NameMangler.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080019#include "Resource.h"
20#include "ResourceTable.h"
21#include "ResourceValues.h"
22#include "StringPiece.h"
23
Adam Lesinskica2fc352015-04-03 12:08:26 -070024#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080025#include <ostream>
26#include <set>
27#include <sstream>
28#include <tuple>
29
30namespace aapt {
31
32// The number of attributes to emit per line in a Styleable array.
33constexpr size_t kAttribsPerLine = 4;
34
Adam Lesinski769de982015-04-10 19:43:55 -070035JavaClassGenerator::JavaClassGenerator(const std::shared_ptr<const ResourceTable>& table,
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080036 Options options) :
37 mTable(table), mOptions(options) {
38}
39
40static void generateHeader(std::ostream& out, const StringPiece16& package) {
41 out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
42 " *\n"
43 " * This class was automatically generated by the\n"
44 " * aapt tool from the resource data it found. It\n"
45 " * should not be modified by hand.\n"
46 " */\n\n";
47 out << "package " << package << ";"
48 << std::endl
49 << std::endl;
50}
51
52static const std::set<StringPiece16> sJavaIdentifiers = {
53 u"abstract", u"assert", u"boolean", u"break", u"byte",
54 u"case", u"catch", u"char", u"class", u"const", u"continue",
55 u"default", u"do", u"double", u"else", u"enum", u"extends",
56 u"final", u"finally", u"float", u"for", u"goto", u"if",
57 u"implements", u"import", u"instanceof", u"int", u"interface",
58 u"long", u"native", u"new", u"package", u"private", u"protected",
59 u"public", u"return", u"short", u"static", u"strictfp", u"super",
60 u"switch", u"synchronized", u"this", u"throw", u"throws",
61 u"transient", u"try", u"void", u"volatile", u"while", u"true",
62 u"false", u"null"
63};
64
65static bool isValidSymbol(const StringPiece16& symbol) {
66 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
67}
68
69/*
70 * Java symbols can not contain . or -, but those are valid in a resource name.
71 * Replace those with '_'.
72 */
73static std::u16string transform(const StringPiece16& symbol) {
74 std::u16string output = symbol.toString();
75 for (char16_t& c : output) {
76 if (c == u'.' || c == u'-') {
77 c = u'_';
78 }
79 }
80 return output;
81}
82
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080083struct GenArgs : ValueVisitorArgs {
Adam Lesinski330edcd2015-05-04 17:40:56 -070084 GenArgs(std::ostream* o, const std::u16string* p, std::u16string* e) :
85 out(o), package(p), entryName(e) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080086 }
87
Adam Lesinski769de982015-04-10 19:43:55 -070088 std::ostream* out;
Adam Lesinski330edcd2015-05-04 17:40:56 -070089 const std::u16string* package;
Adam Lesinski769de982015-04-10 19:43:55 -070090 std::u16string* entryName;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080091};
92
93void JavaClassGenerator::visit(const Styleable& styleable, ValueVisitorArgs& a) {
94 const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
Adam Lesinski769de982015-04-10 19:43:55 -070095 std::ostream* out = static_cast<GenArgs&>(a).out;
Adam Lesinski330edcd2015-05-04 17:40:56 -070096 const std::u16string* package = static_cast<GenArgs&>(a).package;
Adam Lesinski769de982015-04-10 19:43:55 -070097 std::u16string* entryName = static_cast<GenArgs&>(a).entryName;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080098
99 // This must be sorted by resource ID.
Adam Lesinski838a6872015-05-01 13:14:05 -0700100 std::vector<std::pair<ResourceId, ResourceNameRef>> sortedAttributes;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800101 sortedAttributes.reserve(styleable.entries.size());
102 for (const auto& attr : styleable.entries) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700103 // If we are not encoding final attributes, the styleable entry may have no ID
104 // if we are building a static library.
105 assert((!mOptions.useFinal || attr.id.isValid()) && "no ID set for Styleable entry");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800106 assert(attr.name.isValid() && "no name set for Styleable entry");
Adam Lesinski838a6872015-05-01 13:14:05 -0700107 sortedAttributes.emplace_back(attr.id, attr.name);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800108 }
109 std::sort(sortedAttributes.begin(), sortedAttributes.end());
110
111 // First we emit the array containing the IDs of each attribute.
Adam Lesinski769de982015-04-10 19:43:55 -0700112 *out << " "
113 << "public static final int[] " << transform(*entryName) << " = {";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800114
115 const size_t attrCount = sortedAttributes.size();
116 for (size_t i = 0; i < attrCount; i++) {
117 if (i % kAttribsPerLine == 0) {
Adam Lesinski769de982015-04-10 19:43:55 -0700118 *out << std::endl << " ";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800119 }
120
Adam Lesinski769de982015-04-10 19:43:55 -0700121 *out << sortedAttributes[i].first;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800122 if (i != attrCount - 1) {
Adam Lesinski769de982015-04-10 19:43:55 -0700123 *out << ", ";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800124 }
125 }
Adam Lesinski769de982015-04-10 19:43:55 -0700126 *out << std::endl << " };" << std::endl;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800127
128 // Now we emit the indices into the array.
129 for (size_t i = 0; i < attrCount; i++) {
Adam Lesinski769de982015-04-10 19:43:55 -0700130 *out << " "
131 << "public static" << finalModifier
Adam Lesinski838a6872015-05-01 13:14:05 -0700132 << " int " << transform(*entryName);
133
134 // We may reference IDs from other packages, so prefix the entry name with
135 // the package.
136 const ResourceNameRef& itemName = sortedAttributes[i].second;
Adam Lesinski330edcd2015-05-04 17:40:56 -0700137 if (itemName.package != *package) {
Adam Lesinski838a6872015-05-01 13:14:05 -0700138 *out << "_" << transform(itemName.package);
139 }
140 *out << "_" << transform(itemName.entry) << " = " << i << ";" << std::endl;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800141 }
142}
143
Adam Lesinski769de982015-04-10 19:43:55 -0700144bool JavaClassGenerator::generateType(const std::u16string& package, size_t packageId,
145 const ResourceTableType& type, std::ostream& out) {
146 const StringPiece finalModifier = mOptions.useFinal ? " final" : "";
147
148 std::u16string unmangledPackage;
149 std::u16string unmangledName;
150 for (const auto& entry : type.entries) {
151 ResourceId id = { packageId, type.typeId, entry->entryId };
152 assert(id.isValid());
153
154 unmangledName = entry->name;
155 if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
156 // The entry name was mangled, and we successfully unmangled it.
157 // Check that we want to emit this symbol.
158 if (package != unmangledPackage) {
159 // Skip the entry if it doesn't belong to the package we're writing.
160 continue;
161 }
162 } else {
163 if (package != mTable->getPackage()) {
164 // We are processing a mangled package name,
165 // but this is a non-mangled resource.
166 continue;
167 }
168 }
169
170 if (!isValidSymbol(unmangledName)) {
171 ResourceNameRef resourceName = { package, type.type, unmangledName };
172 std::stringstream err;
173 err << "invalid symbol name '" << resourceName << "'";
174 mError = err.str();
175 return false;
176 }
177
178 if (type.type == ResourceType::kStyleable) {
179 assert(!entry->values.empty());
Adam Lesinski330edcd2015-05-04 17:40:56 -0700180 entry->values.front().value->accept(*this, GenArgs{ &out, &package, &unmangledName });
Adam Lesinski769de982015-04-10 19:43:55 -0700181 } else {
182 out << " " << "public static" << finalModifier
183 << " int " << transform(unmangledName) << " = " << id << ";" << std::endl;
184 }
185 }
186 return true;
187}
188
189bool JavaClassGenerator::generate(const std::u16string& package, std::ostream& out) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800190 const size_t packageId = mTable->getPackageId();
191
Adam Lesinski769de982015-04-10 19:43:55 -0700192 generateHeader(out, package);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800193
194 out << "public final class R {" << std::endl;
195
196 for (const auto& type : *mTable) {
197 out << " public static final class " << type->type << " {" << std::endl;
Adam Lesinski769de982015-04-10 19:43:55 -0700198 if (!generateType(package, packageId, *type, out)) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800199 return false;
200 }
201 out << " }" << std::endl;
202 }
203
204 out << "}" << std::endl;
205 return true;
206}
207
208} // namespace aapt