blob: 3ba4dd880607c0aaf35893b8585bee16a2c368ae [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 Lesinski418763f2017-04-11 17:36:53 -070025#include "android-base/errors.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070026#include "android-base/logging.h"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080027#include "android-base/stringprintf.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080028#include "androidfw/StringPiece.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070029
30#include "NameMangler.h"
31#include "Resource.h"
32#include "ResourceTable.h"
33#include "ResourceValues.h"
Adam Lesinski1e4b0e52017-04-27 15:01:10 -070034#include "SdkConstants.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070035#include "ValueVisitor.h"
36#include "java/AnnotationProcessor.h"
37#include "java/ClassDefinition.h"
38#include "process/SymbolTable.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080039
40using android::StringPiece;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080041using android::base::StringPrintf;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070042
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043namespace aapt {
44
Adam Lesinskid0f116b2016-07-08 15:00:32 -070045static const std::set<StringPiece> sJavaIdentifiers = {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070046 "abstract", "assert", "boolean", "break", "byte",
47 "case", "catch", "char", "class", "const",
48 "continue", "default", "do", "double", "else",
49 "enum", "extends", "final", "finally", "float",
50 "for", "goto", "if", "implements", "import",
51 "instanceof", "int", "interface", "long", "native",
52 "new", "package", "private", "protected", "public",
53 "return", "short", "static", "strictfp", "super",
54 "switch", "synchronized", "this", "throw", "throws",
55 "transient", "try", "void", "volatile", "while",
56 "true", "false", "null"};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080057
Adam Lesinskice5e56e2016-10-21 17:56:45 -070058static bool IsValidSymbol(const StringPiece& symbol) {
59 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080060}
61
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080062// Java symbols can not contain . or -, but those are valid in a resource name.
63// Replace those with '_'.
Adam Koskidc21dea2017-07-21 10:55:27 -070064std::string JavaClassGenerator::TransformToFieldName(const StringPiece& symbol) {
Adam Lesinskid5083f62017-01-16 15:07:21 -080065 std::string output = symbol.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070066 for (char& c : output) {
67 if (c == '.' || c == '-') {
68 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080069 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070070 }
71 return output;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080072}
73
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080074// Transforms an attribute in a styleable to the Java field name:
75//
76// <declare-styleable name="Foo">
77// <attr name="android:bar" />
78// <attr name="bar" />
79// </declare-styleable>
80//
81// Foo_android_bar
82// Foo_bar
83static std::string TransformNestedAttr(const ResourceNameRef& attr_name,
84 const std::string& styleable_class_name,
85 const StringPiece& package_name_to_generate) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070086 std::string output = styleable_class_name;
Adam Lesinski74605cd2016-03-03 15:39:50 -080087
Adam Lesinskice5e56e2016-10-21 17:56:45 -070088 // We may reference IDs from other packages, so prefix the entry name with
89 // the package.
90 if (!attr_name.package.empty() &&
91 package_name_to_generate != attr_name.package) {
Adam Koskidc21dea2017-07-21 10:55:27 -070092 output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.package);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070093 }
Adam Koskidc21dea2017-07-21 10:55:27 -070094 output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.entry);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070095 return output;
Adam Lesinski74605cd2016-03-03 15:39:50 -080096}
97
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080098static void AddAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070099 const uint32_t type_mask = attr->type_mask;
100 if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
101 processor->AppendComment(
102 "<p>May be a reference to another resource, in the form\n"
103 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
104 "theme\n"
105 "attribute in the form\n"
106 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
107 }
108
109 if (type_mask & android::ResTable_map::TYPE_STRING) {
110 processor->AppendComment(
111 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
112 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
113 }
114
115 if (type_mask & android::ResTable_map::TYPE_INTEGER) {
116 processor->AppendComment(
117 "<p>May be an integer value, such as \"<code>100</code>\".");
118 }
119
120 if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
121 processor->AppendComment(
122 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
123 "\"<code>false</code>\".");
124 }
125
126 if (type_mask & android::ResTable_map::TYPE_COLOR) {
127 processor->AppendComment(
128 "<p>May be a color value, in the form of "
129 "\"<code>#<i>rgb</i></code>\",\n"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800130 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code>\", or \n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700131 "\"<code>#<i>aarrggbb</i></code>\".");
132 }
133
134 if (type_mask & android::ResTable_map::TYPE_FLOAT) {
135 processor->AppendComment(
136 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
137 }
138
139 if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
140 processor->AppendComment(
141 "<p>May be a dimension value, which is a floating point number "
142 "appended with a\n"
143 "unit such as \"<code>14.5sp</code>\".\n"
144 "Available units are: px (pixels), dp (density-independent pixels),\n"
145 "sp (scaled pixels based on preferred font size), in (inches), and\n"
146 "mm (millimeters).");
147 }
148
149 if (type_mask & android::ResTable_map::TYPE_FRACTION) {
150 processor->AppendComment(
151 "<p>May be a fractional value, which is a floating point number "
152 "appended with\n"
153 "either % or %p, such as \"<code>14.5%</code>\".\n"
154 "The % suffix always means a percentage of the base size;\n"
155 "the optional %p suffix provides a size relative to some parent "
156 "container.");
157 }
158
159 if (type_mask &
160 (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
161 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
162 processor->AppendComment(
163 "<p>Must be one or more (separated by '|') of the following "
164 "constant values.</p>");
165 } else {
166 processor->AppendComment(
167 "<p>Must be one of the following constant values.</p>");
Adam Lesinski76565542016-03-10 21:55:04 -0800168 }
169
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700170 processor->AppendComment(
171 "<table>\n<colgroup align=\"left\" />\n"
172 "<colgroup align=\"left\" />\n"
173 "<colgroup align=\"left\" />\n"
174 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
175 for (const Attribute::Symbol& symbol : attr->symbols) {
176 std::stringstream line;
177 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
178 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
179 << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
180 << "</td></tr>";
181 processor->AppendComment(line.str());
Adam Lesinski76565542016-03-10 21:55:04 -0800182 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700183 processor->AppendComment("</table>");
184 }
Adam Lesinski76565542016-03-10 21:55:04 -0800185}
186
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700187JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
188 ResourceTable* table,
189 const JavaClassGeneratorOptions& options)
190 : context_(context), table_(table), options_(options) {}
191
192bool JavaClassGenerator::SkipSymbol(SymbolState state) {
193 switch (options_.types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700194 case JavaClassGeneratorOptions::SymbolTypes::kAll:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700195 return false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700196 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700197 return state == SymbolState::kUndefined;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700198 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700199 return state != SymbolState::kPublic;
200 }
201 return true;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700202}
203
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800204// Whether or not to skip writing this symbol.
205bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
206 return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
207 !symbol.value().is_public);
208}
209
Adam Lesinski74605cd2016-03-03 15:39:50 -0800210struct StyleableAttr {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800211 const Reference* attr_ref = nullptr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700212 std::string field_name;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800213 Maybe<SymbolTable::Symbol> symbol;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800214};
215
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800216static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
217 const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
218 const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700219 if (lhs_id < rhs_id) {
220 return true;
221 } else if (lhs_id > rhs_id) {
222 return false;
223 } else {
224 return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
225 }
226}
227
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800228void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
229 const Styleable& styleable,
230 const StringPiece& package_name_to_generate,
231 ClassDefinition* out_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700232 MethodDefinition* out_rewrite_method,
233 std::ostream* out_r_txt) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800234 const std::string array_field_name = TransformToFieldName(name.entry);
235 std::unique_ptr<ResourceArrayMember> array_def =
236 util::make_unique<ResourceArrayMember>(array_field_name);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700237
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800238 // The array must be sorted by resource ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700239 std::vector<StyleableAttr> sorted_attributes;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800240 sorted_attributes.reserve(styleable.entries.size());
241 for (const auto& attr : styleable.entries) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700242 // If we are not encoding final attributes, the styleable entry may have no
243 // ID if we are building a static library.
244 CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
245 CHECK(bool(attr.name)) << "no name set for Styleable entry";
246
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800247 // We will need the unmangled, transformed name in the comments and the field,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700248 // so create it once and cache it in this StyleableAttr data structure.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800249 StyleableAttr styleable_attr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700250 styleable_attr.attr_ref = &attr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700251
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800252 // The field name for this attribute is prefixed by the name of this styleable and
253 // the package it comes from.
254 styleable_attr.field_name =
255 TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700256
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800257 // Look up the symbol so that we can write out in the comments what are possible legal values
258 // for this attribute.
259 const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700260 if (symbol && symbol->attribute) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800261 // Copy the symbol data structure because the returned instance can be destroyed.
262 styleable_attr.symbol = *symbol;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700263 }
264 sorted_attributes.push_back(std::move(styleable_attr));
265 }
266
267 // Sort the attributes by ID.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800268 std::sort(sorted_attributes.begin(), sorted_attributes.end());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700269
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800270 // Build the JavaDoc comment for the Styleable array. This has references to child attributes
271 // and what possible values can be used for them.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700272 const size_t attr_count = sorted_attributes.size();
273 if (attr_count > 0) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700274 std::stringstream styleable_comment;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800275 if (!styleable.GetComment().empty()) {
276 styleable_comment << styleable.GetComment() << "\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800277 } else {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800278 // Apply a default intro comment if the styleable has no comments of its own.
279 styleable_comment << "Attributes that can be used with a " << array_field_name << ".\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800280 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700281
282 styleable_comment << "<p>Includes the following attributes:</p>\n"
283 "<table>\n"
284 "<colgroup align=\"left\" />\n"
285 "<colgroup align=\"left\" />\n"
286 "<tr><th>Attribute</th><th>Description</th></tr>\n";
287
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800288 // Build the table of attributes with their links and names.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700289 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800290 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700291 continue;
292 }
293
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800294 StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700295 if (attr_comment_line.contains("@removed")) {
296 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800297 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700298 continue;
299 }
300
301 const ResourceName& attr_name = entry.attr_ref->name.value();
Adam Lesinskie967d3f2017-07-24 18:19:36 -0700302 styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
303 << (!attr_name.package.empty() ? attr_name.package
304 : context_->GetCompilationPackage())
305 << ":" << attr_name.entry << "}</code></td>";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700306
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800307 // Only use the comment up until the first '.'. This is to stay compatible with
308 // the way old AAPT did it (presumably to keep it short and to avoid including
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700309 // annotations like @hide which would affect this Styleable).
Adam Lesinskie967d3f2017-07-24 18:19:36 -0700310 styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
311 << "</td></tr>\n";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700312 }
313 styleable_comment << "</table>\n";
314
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800315 // Generate the @see lines for each attribute.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700316 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800317 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700318 continue;
319 }
320 styleable_comment << "@see #" << entry.field_name << "\n";
321 }
322
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800323 array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700324 }
325
Adam Lesinski418763f2017-04-11 17:36:53 -0700326 if (out_r_txt != nullptr) {
327 *out_r_txt << "int[] styleable " << array_field_name << " {";
328 }
329
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700330 // Add the ResourceIds to the array member.
Adam Lesinski418763f2017-04-11 17:36:53 -0700331 for (size_t i = 0; i < attr_count; i++) {
332 const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800333 array_def->AddElement(id);
Adam Lesinski418763f2017-04-11 17:36:53 -0700334
335 if (out_r_txt != nullptr) {
336 if (i != 0) {
337 *out_r_txt << ",";
338 }
339 *out_r_txt << " " << id;
340 }
341 }
342
343 if (out_r_txt != nullptr) {
344 *out_r_txt << " }\n";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700345 }
346
347 // Add the Styleable array to the Styleable class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800348 out_class_def->AddMember(std::move(array_def));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700349
350 // Now we emit the indices into the array.
351 for (size_t i = 0; i < attr_count; i++) {
352 const StyleableAttr& styleable_attr = sorted_attributes[i];
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800353 if (SkipSymbol(styleable_attr.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700354 continue;
355 }
356
357 StringPiece comment = styleable_attr.attr_ref->GetComment();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800358 if (styleable_attr.symbol.value().attribute && comment.empty()) {
359 comment = styleable_attr.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700360 }
361
362 if (comment.contains("@removed")) {
363 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800364 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700365 continue;
366 }
367
368 const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
369
370 StringPiece package_name = attr_name.package;
371 if (package_name.empty()) {
372 package_name = context_->GetCompilationPackage();
373 }
374
375 std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
376 sorted_attributes[i].field_name, static_cast<uint32_t>(i));
377
378 AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
379
380 if (!comment.empty()) {
381 attr_processor->AppendComment("<p>\n@attr description");
382 attr_processor->AppendComment(comment);
383 } else {
384 std::stringstream default_comment;
385 default_comment << "<p>This symbol is the offset where the "
386 << "{@link " << package_name << ".R.attr#"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800387 << TransformToFieldName(attr_name.entry) << "}\n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700388 << "attribute's value can be found in the "
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800389 << "{@link #" << array_field_name << "} array.";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700390 attr_processor->AppendComment(default_comment.str());
391 }
392
393 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800394 AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700395 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800396 attr_processor->AppendComment(
397 StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700398
Adam Lesinski418763f2017-04-11 17:36:53 -0700399 if (out_r_txt != nullptr) {
400 *out_r_txt << StringPrintf("int styleable %s %d\n", sorted_attributes[i].field_name.data(),
401 (int)i);
402 }
403
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800404 out_class_def->AddMember(std::move(index_member));
405 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700406
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800407 // If there is a rewrite method to generate, add the statements that rewrite package IDs
408 // for this styleable.
409 if (out_rewrite_method != nullptr) {
410 out_rewrite_method->AppendStatement(
411 StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
412 out_rewrite_method->AppendStatement(
413 StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
414 out_rewrite_method->AppendStatement(
415 StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (p << 24);",
416 array_field_name.data(), array_field_name.data()));
417 out_rewrite_method->AppendStatement(" }");
418 out_rewrite_method->AppendStatement("}");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700419 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800420}
421
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800422void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
423 const ResourceEntry& entry, ClassDefinition* out_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700424 MethodDefinition* out_rewrite_method,
425 std::ostream* out_r_txt) {
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700426 ResourceId real_id = id;
427 if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
428 id.package_id() > kAppPackageId) {
429 real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
430 }
431
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800432 const std::string field_name = TransformToFieldName(name.entry);
433 std::unique_ptr<ResourceMember> resource_member =
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700434 util::make_unique<ResourceMember>(field_name, real_id);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800435
436 // Build the comments and annotations for this entry.
437 AnnotationProcessor* processor = resource_member->GetCommentBuilder();
438
439 // Add the comments from any <public> tags.
440 if (entry.symbol_status.state != SymbolState::kUndefined) {
441 processor->AppendComment(entry.symbol_status.comment);
442 }
443
444 // Add the comments from all configurations of this entry.
445 for (const auto& config_value : entry.values) {
446 processor->AppendComment(config_value->value->GetComment());
447 }
448
449 // If this is an Attribute, append the format Javadoc.
450 if (!entry.values.empty()) {
451 if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
452 // We list out the available values for the given attribute.
453 AddAttributeFormatDoc(processor, attr);
454 }
455 }
456
457 out_class_def->AddMember(std::move(resource_member));
458
Adam Lesinski418763f2017-04-11 17:36:53 -0700459 if (out_r_txt != nullptr) {
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700460 *out_r_txt << "int " << name.type << " " << field_name << " " << real_id << "\n";
Adam Lesinski418763f2017-04-11 17:36:53 -0700461 }
462
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800463 if (out_rewrite_method != nullptr) {
464 const StringPiece& type_str = ToString(name.type);
465 out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
466 type_str.data(), field_name.data(),
467 type_str.data(), field_name.data()));
468 }
469}
470
471Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
472 const StringPiece& package_name_to_generate,
473 const ResourceEntry& entry) {
474 if (SkipSymbol(entry.symbol_status.state)) {
475 return {};
476 }
477
478 std::string unmangled_package;
479 std::string unmangled_name = entry.name;
480 if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
481 // The entry name was mangled, and we successfully unmangled it.
482 // Check that we want to emit this symbol.
Adam Lesinski1ef0fa92017-08-15 21:32:49 -0700483 if (package_name_to_generate != unmangled_package) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800484 // Skip the entry if it doesn't belong to the package we're writing.
485 return {};
486 }
487 } else if (package_name_to_generate != package_name) {
488 // We are processing a mangled package name,
489 // but this is a non-mangled resource.
490 return {};
491 }
492 return {std::move(unmangled_name)};
493}
494
495bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate,
496 const ResourceTablePackage& package,
497 const ResourceTableType& type,
498 ClassDefinition* out_type_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700499 MethodDefinition* out_rewrite_method_def,
500 std::ostream* out_r_txt) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800501 for (const auto& entry : type.entries) {
502 const Maybe<std::string> unmangled_name =
503 UnmangleResource(package.name, package_name_to_generate, *entry);
504 if (!unmangled_name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700505 continue;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800506 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700507
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800508 // Create an ID if there is one (static libraries don't need one).
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700509 ResourceId id;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800510 if (package.id && type.id && entry->id) {
511 id = ResourceId(package.id.value(), type.id.value(), entry->id.value());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800512 }
513
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800514 // We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
515 const ResourceNameRef resource_name(
516 package_name_to_generate,
517 type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type,
518 unmangled_name.value());
Adam Lesinskib274e352015-11-06 15:14:35 -0800519
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800520 // Check to see if the unmangled name is a valid Java name (not a keyword).
521 if (!IsValidSymbol(unmangled_name.value())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700522 std::stringstream err;
523 err << "invalid symbol name '" << resource_name << "'";
524 error_ = err.str();
525 return false;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700526 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700527
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800528 if (resource_name.type == ResourceType::kStyleable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700529 CHECK(!entry->values.empty());
530
531 const Styleable* styleable =
532 static_cast<const Styleable*>(entry->values.front()->value.get());
533
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800534 ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700535 out_rewrite_method_def, out_r_txt);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700536 } else {
Adam Lesinski418763f2017-04-11 17:36:53 -0700537 ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
538 out_r_txt);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700539 }
540 }
541 return true;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700542}
543
Adam Lesinski418763f2017-04-11 17:36:53 -0700544bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out,
545 std::ostream* out_r_txt) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700546 return Generate(package_name_to_generate, package_name_to_generate, out);
Adam Lesinski769de982015-04-10 19:43:55 -0700547}
548
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800549static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
550 AnnotationProcessor* processor) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700551 for (const std::string& annotation : annotations) {
552 std::string proper_annotation = "@";
553 proper_annotation += annotation;
554 processor->AppendComment(proper_annotation);
555 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700556}
557
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700558bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
Adam Lesinski418763f2017-04-11 17:36:53 -0700559 const StringPiece& out_package_name, std::ostream* out,
560 std::ostream* out_r_txt) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800561 ClassDefinition r_class("R", ClassQualifier::kNone, true);
562 std::unique_ptr<MethodDefinition> rewrite_method;
563
564 // Generate an onResourcesLoaded() callback if requested.
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800565 if (options_.rewrite_callback_options) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800566 rewrite_method =
567 util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800568 for (const std::string& package_to_callback :
569 options_.rewrite_callback_options.value().packages_to_callback) {
570 rewrite_method->AppendStatement(
571 StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
572 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800573 }
Adam Lesinski3524a232016-04-01 19:19:24 -0700574
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700575 for (const auto& package : table_->packages) {
576 for (const auto& type : package->types) {
577 if (type->type == ResourceType::kAttrPrivate) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800578 // We generate these as part of the kAttr type, so skip them here.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700579 continue;
580 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800581
Adam Lesinski1ef0fa92017-08-15 21:32:49 -0700582 // Stay consistent with AAPT and generate an empty type class if the R class is public.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700583 const bool force_creation_if_empty =
584 (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800585
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800586 std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
587 ToString(type->type), ClassQualifier::kStatic, force_creation_if_empty);
588 if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
Adam Lesinski418763f2017-04-11 17:36:53 -0700589 rewrite_method.get(), out_r_txt)) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700590 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700591 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700592
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700593 if (type->type == ResourceType::kAttr) {
594 // Also include private attributes in this same class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800595 const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700596 if (priv_type) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800597 if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
Adam Lesinski418763f2017-04-11 17:36:53 -0700598 rewrite_method.get(), out_r_txt)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700599 return false;
600 }
601 }
602 }
603
604 if (type->type == ResourceType::kStyleable &&
605 options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
606 // When generating a public R class, we don't want Styleable to be part
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800607 // of the API. It is only emitted for documentation purposes.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700608 class_def->GetCommentBuilder()->AppendComment("@doconly");
609 }
610
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800611 AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700612
613 r_class.AddMember(std::move(class_def));
614 }
615 }
616
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800617 if (rewrite_method != nullptr) {
618 r_class.AddMember(std::move(rewrite_method));
619 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700620
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800621 AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
622
623 if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700624 return false;
625 }
626
627 out->flush();
Adam Lesinski418763f2017-04-11 17:36:53 -0700628
629 if (out_r_txt != nullptr) {
630 out_r_txt->flush();
631
632 if (!*out_r_txt) {
633 error_ = android::base::SystemErrorCodeToString(errno);
634 return false;
635 }
636 }
637
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700638 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800639}
640
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700641} // namespace aapt