blob: 68bdb959ca079107231dbb7becb30100ca7c1598 [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
Adam Lesinskica5638f2015-10-21 14:42:43 -070017#include "java/JavaClassGenerator.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018
Adam Lesinskica2fc352015-04-03 12:08:26 -070019#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <ostream>
21#include <set>
22#include <sstream>
23#include <tuple>
24
Adam Lesinskice5e56e2016-10-21 17:56:45 -070025#include "android-base/logging.h"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080026#include "android-base/stringprintf.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080027#include "androidfw/StringPiece.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070028
29#include "NameMangler.h"
30#include "Resource.h"
31#include "ResourceTable.h"
32#include "ResourceValues.h"
33#include "ValueVisitor.h"
34#include "java/AnnotationProcessor.h"
35#include "java/ClassDefinition.h"
36#include "process/SymbolTable.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080037
38using android::StringPiece;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080039using android::base::StringPrintf;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070040
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080041namespace aapt {
42
Adam Lesinskid0f116b2016-07-08 15:00:32 -070043static const std::set<StringPiece> sJavaIdentifiers = {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070044 "abstract", "assert", "boolean", "break", "byte",
45 "case", "catch", "char", "class", "const",
46 "continue", "default", "do", "double", "else",
47 "enum", "extends", "final", "finally", "float",
48 "for", "goto", "if", "implements", "import",
49 "instanceof", "int", "interface", "long", "native",
50 "new", "package", "private", "protected", "public",
51 "return", "short", "static", "strictfp", "super",
52 "switch", "synchronized", "this", "throw", "throws",
53 "transient", "try", "void", "volatile", "while",
54 "true", "false", "null"};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080055
Adam Lesinskice5e56e2016-10-21 17:56:45 -070056static bool IsValidSymbol(const StringPiece& symbol) {
57 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080058}
59
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080060// Java symbols can not contain . or -, but those are valid in a resource name.
61// Replace those with '_'.
62static std::string TransformToFieldName(const StringPiece& symbol) {
Adam Lesinskid5083f62017-01-16 15:07:21 -080063 std::string output = symbol.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070064 for (char& c : output) {
65 if (c == '.' || c == '-') {
66 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 }
69 return output;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080070}
71
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080072// Transforms an attribute in a styleable to the Java field name:
73//
74// <declare-styleable name="Foo">
75// <attr name="android:bar" />
76// <attr name="bar" />
77// </declare-styleable>
78//
79// Foo_android_bar
80// Foo_bar
81static std::string TransformNestedAttr(const ResourceNameRef& attr_name,
82 const std::string& styleable_class_name,
83 const StringPiece& package_name_to_generate) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070084 std::string output = styleable_class_name;
Adam Lesinski74605cd2016-03-03 15:39:50 -080085
Adam Lesinskice5e56e2016-10-21 17:56:45 -070086 // We may reference IDs from other packages, so prefix the entry name with
87 // the package.
88 if (!attr_name.package.empty() &&
89 package_name_to_generate != attr_name.package) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080090 output += "_" + TransformToFieldName(attr_name.package);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070091 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080092 output += "_" + TransformToFieldName(attr_name.entry);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070093 return output;
Adam Lesinski74605cd2016-03-03 15:39:50 -080094}
95
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080096static void AddAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070097 const uint32_t type_mask = attr->type_mask;
98 if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
99 processor->AppendComment(
100 "<p>May be a reference to another resource, in the form\n"
101 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
102 "theme\n"
103 "attribute in the form\n"
104 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
105 }
106
107 if (type_mask & android::ResTable_map::TYPE_STRING) {
108 processor->AppendComment(
109 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
110 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
111 }
112
113 if (type_mask & android::ResTable_map::TYPE_INTEGER) {
114 processor->AppendComment(
115 "<p>May be an integer value, such as \"<code>100</code>\".");
116 }
117
118 if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
119 processor->AppendComment(
120 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
121 "\"<code>false</code>\".");
122 }
123
124 if (type_mask & android::ResTable_map::TYPE_COLOR) {
125 processor->AppendComment(
126 "<p>May be a color value, in the form of "
127 "\"<code>#<i>rgb</i></code>\",\n"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800128 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code>\", or \n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700129 "\"<code>#<i>aarrggbb</i></code>\".");
130 }
131
132 if (type_mask & android::ResTable_map::TYPE_FLOAT) {
133 processor->AppendComment(
134 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
135 }
136
137 if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
138 processor->AppendComment(
139 "<p>May be a dimension value, which is a floating point number "
140 "appended with a\n"
141 "unit such as \"<code>14.5sp</code>\".\n"
142 "Available units are: px (pixels), dp (density-independent pixels),\n"
143 "sp (scaled pixels based on preferred font size), in (inches), and\n"
144 "mm (millimeters).");
145 }
146
147 if (type_mask & android::ResTable_map::TYPE_FRACTION) {
148 processor->AppendComment(
149 "<p>May be a fractional value, which is a floating point number "
150 "appended with\n"
151 "either % or %p, such as \"<code>14.5%</code>\".\n"
152 "The % suffix always means a percentage of the base size;\n"
153 "the optional %p suffix provides a size relative to some parent "
154 "container.");
155 }
156
157 if (type_mask &
158 (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
159 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
160 processor->AppendComment(
161 "<p>Must be one or more (separated by '|') of the following "
162 "constant values.</p>");
163 } else {
164 processor->AppendComment(
165 "<p>Must be one of the following constant values.</p>");
Adam Lesinski76565542016-03-10 21:55:04 -0800166 }
167
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700168 processor->AppendComment(
169 "<table>\n<colgroup align=\"left\" />\n"
170 "<colgroup align=\"left\" />\n"
171 "<colgroup align=\"left\" />\n"
172 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
173 for (const Attribute::Symbol& symbol : attr->symbols) {
174 std::stringstream line;
175 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
176 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
177 << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
178 << "</td></tr>";
179 processor->AppendComment(line.str());
Adam Lesinski76565542016-03-10 21:55:04 -0800180 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700181 processor->AppendComment("</table>");
182 }
Adam Lesinski76565542016-03-10 21:55:04 -0800183}
184
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700185JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
186 ResourceTable* table,
187 const JavaClassGeneratorOptions& options)
188 : context_(context), table_(table), options_(options) {}
189
190bool JavaClassGenerator::SkipSymbol(SymbolState state) {
191 switch (options_.types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700192 case JavaClassGeneratorOptions::SymbolTypes::kAll:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700193 return false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700194 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700195 return state == SymbolState::kUndefined;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700196 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700197 return state != SymbolState::kPublic;
198 }
199 return true;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700200}
201
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800202// Whether or not to skip writing this symbol.
203bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
204 return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
205 !symbol.value().is_public);
206}
207
Adam Lesinski74605cd2016-03-03 15:39:50 -0800208struct StyleableAttr {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800209 const Reference* attr_ref = nullptr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700210 std::string field_name;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800211 Maybe<SymbolTable::Symbol> symbol;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800212};
213
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800214static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
215 const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
216 const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700217 if (lhs_id < rhs_id) {
218 return true;
219 } else if (lhs_id > rhs_id) {
220 return false;
221 } else {
222 return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
223 }
224}
225
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800226void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
227 const Styleable& styleable,
228 const StringPiece& package_name_to_generate,
229 ClassDefinition* out_class_def,
230 MethodDefinition* out_rewrite_method) {
231 const std::string array_field_name = TransformToFieldName(name.entry);
232 std::unique_ptr<ResourceArrayMember> array_def =
233 util::make_unique<ResourceArrayMember>(array_field_name);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700234
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800235 // The array must be sorted by resource ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700236 std::vector<StyleableAttr> sorted_attributes;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800237 sorted_attributes.reserve(styleable.entries.size());
238 for (const auto& attr : styleable.entries) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700239 // If we are not encoding final attributes, the styleable entry may have no
240 // ID if we are building a static library.
241 CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
242 CHECK(bool(attr.name)) << "no name set for Styleable entry";
243
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800244 // We will need the unmangled, transformed name in the comments and the field,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700245 // so create it once and cache it in this StyleableAttr data structure.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800246 StyleableAttr styleable_attr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700247 styleable_attr.attr_ref = &attr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700248
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800249 // The field name for this attribute is prefixed by the name of this styleable and
250 // the package it comes from.
251 styleable_attr.field_name =
252 TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700253
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800254 // Look up the symbol so that we can write out in the comments what are possible legal values
255 // for this attribute.
256 const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700257 if (symbol && symbol->attribute) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800258 // Copy the symbol data structure because the returned instance can be destroyed.
259 styleable_attr.symbol = *symbol;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700260 }
261 sorted_attributes.push_back(std::move(styleable_attr));
262 }
263
264 // Sort the attributes by ID.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800265 std::sort(sorted_attributes.begin(), sorted_attributes.end());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700266
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800267 // Build the JavaDoc comment for the Styleable array. This has references to child attributes
268 // and what possible values can be used for them.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700269 const size_t attr_count = sorted_attributes.size();
270 if (attr_count > 0) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700271 std::stringstream styleable_comment;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800272 if (!styleable.GetComment().empty()) {
273 styleable_comment << styleable.GetComment() << "\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800274 } else {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800275 // Apply a default intro comment if the styleable has no comments of its own.
276 styleable_comment << "Attributes that can be used with a " << array_field_name << ".\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800277 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700278
279 styleable_comment << "<p>Includes the following attributes:</p>\n"
280 "<table>\n"
281 "<colgroup align=\"left\" />\n"
282 "<colgroup align=\"left\" />\n"
283 "<tr><th>Attribute</th><th>Description</th></tr>\n";
284
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800285 // Build the table of attributes with their links and names.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700286 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800287 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700288 continue;
289 }
290
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800291 StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700292 if (attr_comment_line.contains("@removed")) {
293 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800294 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700295 continue;
296 }
297
298 const ResourceName& attr_name = entry.attr_ref->name.value();
299 styleable_comment << "<tr><td>";
300 styleable_comment << "<code>{@link #" << entry.field_name << " "
301 << (!attr_name.package.empty()
302 ? attr_name.package
303 : context_->GetCompilationPackage())
304 << ":" << attr_name.entry << "}</code>";
305 styleable_comment << "</td>";
306
307 styleable_comment << "<td>";
308
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800309 // Only use the comment up until the first '.'. This is to stay compatible with
310 // the way old AAPT did it (presumably to keep it short and to avoid including
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700311 // annotations like @hide which would affect this Styleable).
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800312 auto iter = std::find(attr_comment_line.begin(), attr_comment_line.end(), '.');
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700313 if (iter != attr_comment_line.end()) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800314 attr_comment_line = attr_comment_line.substr(0, (iter - attr_comment_line.begin()) + 1);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700315 }
316 styleable_comment << attr_comment_line << "</td></tr>\n";
317 }
318 styleable_comment << "</table>\n";
319
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800320 // Generate the @see lines for each attribute.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700321 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800322 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700323 continue;
324 }
325 styleable_comment << "@see #" << entry.field_name << "\n";
326 }
327
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800328 array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700329 }
330
331 // Add the ResourceIds to the array member.
332 for (const StyleableAttr& styleable_attr : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800333 const ResourceId id = styleable_attr.attr_ref->id.value_or_default(ResourceId(0));
334 array_def->AddElement(id);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700335 }
336
337 // Add the Styleable array to the Styleable class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800338 out_class_def->AddMember(std::move(array_def));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700339
340 // Now we emit the indices into the array.
341 for (size_t i = 0; i < attr_count; i++) {
342 const StyleableAttr& styleable_attr = sorted_attributes[i];
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800343 if (SkipSymbol(styleable_attr.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700344 continue;
345 }
346
347 StringPiece comment = styleable_attr.attr_ref->GetComment();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800348 if (styleable_attr.symbol.value().attribute && comment.empty()) {
349 comment = styleable_attr.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700350 }
351
352 if (comment.contains("@removed")) {
353 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800354 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700355 continue;
356 }
357
358 const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
359
360 StringPiece package_name = attr_name.package;
361 if (package_name.empty()) {
362 package_name = context_->GetCompilationPackage();
363 }
364
365 std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
366 sorted_attributes[i].field_name, static_cast<uint32_t>(i));
367
368 AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
369
370 if (!comment.empty()) {
371 attr_processor->AppendComment("<p>\n@attr description");
372 attr_processor->AppendComment(comment);
373 } else {
374 std::stringstream default_comment;
375 default_comment << "<p>This symbol is the offset where the "
376 << "{@link " << package_name << ".R.attr#"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800377 << TransformToFieldName(attr_name.entry) << "}\n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700378 << "attribute's value can be found in the "
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800379 << "{@link #" << array_field_name << "} array.";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700380 attr_processor->AppendComment(default_comment.str());
381 }
382
383 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800384 AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700385 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800386 attr_processor->AppendComment(
387 StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700388
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800389 out_class_def->AddMember(std::move(index_member));
390 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700391
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800392 // If there is a rewrite method to generate, add the statements that rewrite package IDs
393 // for this styleable.
394 if (out_rewrite_method != nullptr) {
395 out_rewrite_method->AppendStatement(
396 StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
397 out_rewrite_method->AppendStatement(
398 StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
399 out_rewrite_method->AppendStatement(
400 StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (p << 24);",
401 array_field_name.data(), array_field_name.data()));
402 out_rewrite_method->AppendStatement(" }");
403 out_rewrite_method->AppendStatement("}");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700404 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800405}
406
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800407void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
408 const ResourceEntry& entry, ClassDefinition* out_class_def,
409 MethodDefinition* out_rewrite_method) {
410 const std::string field_name = TransformToFieldName(name.entry);
411 std::unique_ptr<ResourceMember> resource_member =
412 util::make_unique<ResourceMember>(field_name, id);
413
414 // Build the comments and annotations for this entry.
415 AnnotationProcessor* processor = resource_member->GetCommentBuilder();
416
417 // Add the comments from any <public> tags.
418 if (entry.symbol_status.state != SymbolState::kUndefined) {
419 processor->AppendComment(entry.symbol_status.comment);
420 }
421
422 // Add the comments from all configurations of this entry.
423 for (const auto& config_value : entry.values) {
424 processor->AppendComment(config_value->value->GetComment());
425 }
426
427 // If this is an Attribute, append the format Javadoc.
428 if (!entry.values.empty()) {
429 if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
430 // We list out the available values for the given attribute.
431 AddAttributeFormatDoc(processor, attr);
432 }
433 }
434
435 out_class_def->AddMember(std::move(resource_member));
436
437 if (out_rewrite_method != nullptr) {
438 const StringPiece& type_str = ToString(name.type);
439 out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
440 type_str.data(), field_name.data(),
441 type_str.data(), field_name.data()));
442 }
443}
444
445Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
446 const StringPiece& package_name_to_generate,
447 const ResourceEntry& entry) {
448 if (SkipSymbol(entry.symbol_status.state)) {
449 return {};
450 }
451
452 std::string unmangled_package;
453 std::string unmangled_name = entry.name;
454 if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
455 // The entry name was mangled, and we successfully unmangled it.
456 // Check that we want to emit this symbol.
457 if (package_name != unmangled_package) {
458 // Skip the entry if it doesn't belong to the package we're writing.
459 return {};
460 }
461 } else if (package_name_to_generate != package_name) {
462 // We are processing a mangled package name,
463 // but this is a non-mangled resource.
464 return {};
465 }
466 return {std::move(unmangled_name)};
467}
468
469bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate,
470 const ResourceTablePackage& package,
471 const ResourceTableType& type,
472 ClassDefinition* out_type_class_def,
473 MethodDefinition* out_rewrite_method_def) {
474 for (const auto& entry : type.entries) {
475 const Maybe<std::string> unmangled_name =
476 UnmangleResource(package.name, package_name_to_generate, *entry);
477 if (!unmangled_name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700478 continue;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800479 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700480
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800481 // Create an ID if there is one (static libraries don't need one).
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700482 ResourceId id;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800483 if (package.id && type.id && entry->id) {
484 id = ResourceId(package.id.value(), type.id.value(), entry->id.value());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800485 }
486
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800487 // We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
488 const ResourceNameRef resource_name(
489 package_name_to_generate,
490 type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type,
491 unmangled_name.value());
Adam Lesinskib274e352015-11-06 15:14:35 -0800492
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800493 // Check to see if the unmangled name is a valid Java name (not a keyword).
494 if (!IsValidSymbol(unmangled_name.value())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700495 std::stringstream err;
496 err << "invalid symbol name '" << resource_name << "'";
497 error_ = err.str();
498 return false;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700499 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700500
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800501 if (resource_name.type == ResourceType::kStyleable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700502 CHECK(!entry->values.empty());
503
504 const Styleable* styleable =
505 static_cast<const Styleable*>(entry->values.front()->value.get());
506
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800507 ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
508 out_rewrite_method_def);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700509 } else {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800510 ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700511 }
512 }
513 return true;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700514}
515
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800516bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700517 return Generate(package_name_to_generate, package_name_to_generate, out);
Adam Lesinski769de982015-04-10 19:43:55 -0700518}
519
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800520static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
521 AnnotationProcessor* processor) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700522 for (const std::string& annotation : annotations) {
523 std::string proper_annotation = "@";
524 proper_annotation += annotation;
525 processor->AppendComment(proper_annotation);
526 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700527}
528
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700529bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
530 const StringPiece& out_package_name,
531 std::ostream* out) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800532 ClassDefinition r_class("R", ClassQualifier::kNone, true);
533 std::unique_ptr<MethodDefinition> rewrite_method;
534
535 // Generate an onResourcesLoaded() callback if requested.
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800536 if (options_.rewrite_callback_options) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800537 rewrite_method =
538 util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800539 for (const std::string& package_to_callback :
540 options_.rewrite_callback_options.value().packages_to_callback) {
541 rewrite_method->AppendStatement(
542 StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
543 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800544 }
Adam Lesinski3524a232016-04-01 19:19:24 -0700545
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700546 for (const auto& package : table_->packages) {
547 for (const auto& type : package->types) {
548 if (type->type == ResourceType::kAttrPrivate) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800549 // We generate these as part of the kAttr type, so skip them here.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700550 continue;
551 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800552
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800553 // Stay consistent with AAPT and generate an empty type class if the R class
554 // is public.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700555 const bool force_creation_if_empty =
556 (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800557
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800558 std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
559 ToString(type->type), ClassQualifier::kStatic, force_creation_if_empty);
560 if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
561 rewrite_method.get())) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700562 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700563 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700564
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700565 if (type->type == ResourceType::kAttr) {
566 // Also include private attributes in this same class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800567 const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700568 if (priv_type) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800569 if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
570 rewrite_method.get())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700571 return false;
572 }
573 }
574 }
575
576 if (type->type == ResourceType::kStyleable &&
577 options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
578 // When generating a public R class, we don't want Styleable to be part
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800579 // of the API. It is only emitted for documentation purposes.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700580 class_def->GetCommentBuilder()->AppendComment("@doconly");
581 }
582
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800583 AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700584
585 r_class.AddMember(std::move(class_def));
586 }
587 }
588
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800589 if (rewrite_method != nullptr) {
590 r_class.AddMember(std::move(rewrite_method));
591 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700592
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800593 AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
594
595 if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700596 return false;
597 }
598
599 out->flush();
600 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800601}
602
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700603} // namespace aapt