blob: 2a23aa9e5372b00a49050fa35fe3424388a5d2fb [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 '_'.
64static std::string 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 Lesinskiceb9b2f2017-02-16 12:05:42 -080092 output += "_" + TransformToFieldName(attr_name.package);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070093 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080094 output += "_" + 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();
302 styleable_comment << "<tr><td>";
303 styleable_comment << "<code>{@link #" << entry.field_name << " "
304 << (!attr_name.package.empty()
305 ? attr_name.package
306 : context_->GetCompilationPackage())
307 << ":" << attr_name.entry << "}</code>";
308 styleable_comment << "</td>";
309
310 styleable_comment << "<td>";
311
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800312 // Only use the comment up until the first '.'. This is to stay compatible with
313 // the way old AAPT did it (presumably to keep it short and to avoid including
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700314 // annotations like @hide which would affect this Styleable).
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800315 auto iter = std::find(attr_comment_line.begin(), attr_comment_line.end(), '.');
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700316 if (iter != attr_comment_line.end()) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800317 attr_comment_line = attr_comment_line.substr(0, (iter - attr_comment_line.begin()) + 1);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700318 }
319 styleable_comment << attr_comment_line << "</td></tr>\n";
320 }
321 styleable_comment << "</table>\n";
322
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800323 // Generate the @see lines for each attribute.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700324 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800325 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700326 continue;
327 }
328 styleable_comment << "@see #" << entry.field_name << "\n";
329 }
330
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800331 array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700332 }
333
Adam Lesinski418763f2017-04-11 17:36:53 -0700334 if (out_r_txt != nullptr) {
335 *out_r_txt << "int[] styleable " << array_field_name << " {";
336 }
337
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700338 // Add the ResourceIds to the array member.
Adam Lesinski418763f2017-04-11 17:36:53 -0700339 for (size_t i = 0; i < attr_count; i++) {
340 const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800341 array_def->AddElement(id);
Adam Lesinski418763f2017-04-11 17:36:53 -0700342
343 if (out_r_txt != nullptr) {
344 if (i != 0) {
345 *out_r_txt << ",";
346 }
347 *out_r_txt << " " << id;
348 }
349 }
350
351 if (out_r_txt != nullptr) {
352 *out_r_txt << " }\n";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700353 }
354
355 // Add the Styleable array to the Styleable class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800356 out_class_def->AddMember(std::move(array_def));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700357
358 // Now we emit the indices into the array.
359 for (size_t i = 0; i < attr_count; i++) {
360 const StyleableAttr& styleable_attr = sorted_attributes[i];
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800361 if (SkipSymbol(styleable_attr.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700362 continue;
363 }
364
365 StringPiece comment = styleable_attr.attr_ref->GetComment();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800366 if (styleable_attr.symbol.value().attribute && comment.empty()) {
367 comment = styleable_attr.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700368 }
369
370 if (comment.contains("@removed")) {
371 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800372 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700373 continue;
374 }
375
376 const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
377
378 StringPiece package_name = attr_name.package;
379 if (package_name.empty()) {
380 package_name = context_->GetCompilationPackage();
381 }
382
383 std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
384 sorted_attributes[i].field_name, static_cast<uint32_t>(i));
385
386 AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
387
388 if (!comment.empty()) {
389 attr_processor->AppendComment("<p>\n@attr description");
390 attr_processor->AppendComment(comment);
391 } else {
392 std::stringstream default_comment;
393 default_comment << "<p>This symbol is the offset where the "
394 << "{@link " << package_name << ".R.attr#"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800395 << TransformToFieldName(attr_name.entry) << "}\n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700396 << "attribute's value can be found in the "
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800397 << "{@link #" << array_field_name << "} array.";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700398 attr_processor->AppendComment(default_comment.str());
399 }
400
401 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800402 AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700403 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800404 attr_processor->AppendComment(
405 StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700406
Adam Lesinski418763f2017-04-11 17:36:53 -0700407 if (out_r_txt != nullptr) {
408 *out_r_txt << StringPrintf("int styleable %s %d\n", sorted_attributes[i].field_name.data(),
409 (int)i);
410 }
411
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800412 out_class_def->AddMember(std::move(index_member));
413 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700414
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800415 // If there is a rewrite method to generate, add the statements that rewrite package IDs
416 // for this styleable.
417 if (out_rewrite_method != nullptr) {
418 out_rewrite_method->AppendStatement(
419 StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
420 out_rewrite_method->AppendStatement(
421 StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
422 out_rewrite_method->AppendStatement(
423 StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (p << 24);",
424 array_field_name.data(), array_field_name.data()));
425 out_rewrite_method->AppendStatement(" }");
426 out_rewrite_method->AppendStatement("}");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700427 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800428}
429
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800430void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
431 const ResourceEntry& entry, ClassDefinition* out_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700432 MethodDefinition* out_rewrite_method,
433 std::ostream* out_r_txt) {
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700434 ResourceId real_id = id;
435 if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
436 id.package_id() > kAppPackageId) {
437 real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
438 }
439
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800440 const std::string field_name = TransformToFieldName(name.entry);
441 std::unique_ptr<ResourceMember> resource_member =
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700442 util::make_unique<ResourceMember>(field_name, real_id);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800443
444 // Build the comments and annotations for this entry.
445 AnnotationProcessor* processor = resource_member->GetCommentBuilder();
446
447 // Add the comments from any <public> tags.
448 if (entry.symbol_status.state != SymbolState::kUndefined) {
449 processor->AppendComment(entry.symbol_status.comment);
450 }
451
452 // Add the comments from all configurations of this entry.
453 for (const auto& config_value : entry.values) {
454 processor->AppendComment(config_value->value->GetComment());
455 }
456
457 // If this is an Attribute, append the format Javadoc.
458 if (!entry.values.empty()) {
459 if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
460 // We list out the available values for the given attribute.
461 AddAttributeFormatDoc(processor, attr);
462 }
463 }
464
465 out_class_def->AddMember(std::move(resource_member));
466
Adam Lesinski418763f2017-04-11 17:36:53 -0700467 if (out_r_txt != nullptr) {
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700468 *out_r_txt << "int " << name.type << " " << field_name << " " << real_id << "\n";
Adam Lesinski418763f2017-04-11 17:36:53 -0700469 }
470
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800471 if (out_rewrite_method != nullptr) {
472 const StringPiece& type_str = ToString(name.type);
473 out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
474 type_str.data(), field_name.data(),
475 type_str.data(), field_name.data()));
476 }
477}
478
479Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
480 const StringPiece& package_name_to_generate,
481 const ResourceEntry& entry) {
482 if (SkipSymbol(entry.symbol_status.state)) {
483 return {};
484 }
485
486 std::string unmangled_package;
487 std::string unmangled_name = entry.name;
488 if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
489 // The entry name was mangled, and we successfully unmangled it.
490 // Check that we want to emit this symbol.
491 if (package_name != unmangled_package) {
492 // Skip the entry if it doesn't belong to the package we're writing.
493 return {};
494 }
495 } else if (package_name_to_generate != package_name) {
496 // We are processing a mangled package name,
497 // but this is a non-mangled resource.
498 return {};
499 }
500 return {std::move(unmangled_name)};
501}
502
503bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate,
504 const ResourceTablePackage& package,
505 const ResourceTableType& type,
506 ClassDefinition* out_type_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700507 MethodDefinition* out_rewrite_method_def,
508 std::ostream* out_r_txt) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800509 for (const auto& entry : type.entries) {
510 const Maybe<std::string> unmangled_name =
511 UnmangleResource(package.name, package_name_to_generate, *entry);
512 if (!unmangled_name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700513 continue;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800514 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700515
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800516 // Create an ID if there is one (static libraries don't need one).
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700517 ResourceId id;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800518 if (package.id && type.id && entry->id) {
519 id = ResourceId(package.id.value(), type.id.value(), entry->id.value());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800520 }
521
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800522 // We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
523 const ResourceNameRef resource_name(
524 package_name_to_generate,
525 type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type,
526 unmangled_name.value());
Adam Lesinskib274e352015-11-06 15:14:35 -0800527
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800528 // Check to see if the unmangled name is a valid Java name (not a keyword).
529 if (!IsValidSymbol(unmangled_name.value())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700530 std::stringstream err;
531 err << "invalid symbol name '" << resource_name << "'";
532 error_ = err.str();
533 return false;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700534 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700535
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800536 if (resource_name.type == ResourceType::kStyleable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700537 CHECK(!entry->values.empty());
538
539 const Styleable* styleable =
540 static_cast<const Styleable*>(entry->values.front()->value.get());
541
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800542 ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700543 out_rewrite_method_def, out_r_txt);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700544 } else {
Adam Lesinski418763f2017-04-11 17:36:53 -0700545 ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
546 out_r_txt);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700547 }
548 }
549 return true;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700550}
551
Adam Lesinski418763f2017-04-11 17:36:53 -0700552bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, std::ostream* out,
553 std::ostream* out_r_txt) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700554 return Generate(package_name_to_generate, package_name_to_generate, out);
Adam Lesinski769de982015-04-10 19:43:55 -0700555}
556
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800557static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
558 AnnotationProcessor* processor) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700559 for (const std::string& annotation : annotations) {
560 std::string proper_annotation = "@";
561 proper_annotation += annotation;
562 processor->AppendComment(proper_annotation);
563 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700564}
565
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700566bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
Adam Lesinski418763f2017-04-11 17:36:53 -0700567 const StringPiece& out_package_name, std::ostream* out,
568 std::ostream* out_r_txt) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800569 ClassDefinition r_class("R", ClassQualifier::kNone, true);
570 std::unique_ptr<MethodDefinition> rewrite_method;
571
572 // Generate an onResourcesLoaded() callback if requested.
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800573 if (options_.rewrite_callback_options) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800574 rewrite_method =
575 util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800576 for (const std::string& package_to_callback :
577 options_.rewrite_callback_options.value().packages_to_callback) {
578 rewrite_method->AppendStatement(
579 StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
580 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800581 }
Adam Lesinski3524a232016-04-01 19:19:24 -0700582
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700583 for (const auto& package : table_->packages) {
584 for (const auto& type : package->types) {
585 if (type->type == ResourceType::kAttrPrivate) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800586 // We generate these as part of the kAttr type, so skip them here.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700587 continue;
588 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800589
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800590 // Stay consistent with AAPT and generate an empty type class if the R class
591 // is public.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700592 const bool force_creation_if_empty =
593 (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800594
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800595 std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
596 ToString(type->type), ClassQualifier::kStatic, force_creation_if_empty);
597 if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
Adam Lesinski418763f2017-04-11 17:36:53 -0700598 rewrite_method.get(), out_r_txt)) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700599 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700600 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700601
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700602 if (type->type == ResourceType::kAttr) {
603 // Also include private attributes in this same class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800604 const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700605 if (priv_type) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800606 if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
Adam Lesinski418763f2017-04-11 17:36:53 -0700607 rewrite_method.get(), out_r_txt)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700608 return false;
609 }
610 }
611 }
612
613 if (type->type == ResourceType::kStyleable &&
614 options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
615 // When generating a public R class, we don't want Styleable to be part
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800616 // of the API. It is only emitted for documentation purposes.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700617 class_def->GetCommentBuilder()->AppendComment("@doconly");
618 }
619
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800620 AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700621
622 r_class.AddMember(std::move(class_def));
623 }
624 }
625
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800626 if (rewrite_method != nullptr) {
627 r_class.AddMember(std::move(rewrite_method));
628 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700629
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800630 AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
631
632 if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700633 return false;
634 }
635
636 out->flush();
Adam Lesinski418763f2017-04-11 17:36:53 -0700637
638 if (out_r_txt != nullptr) {
639 out_r_txt->flush();
640
641 if (!*out_r_txt) {
642 error_ = android::base::SystemErrorCodeToString(errno);
643 return false;
644 }
645 }
646
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700647 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800648}
649
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700650} // namespace aapt