blob: 9861770083a2b72f1a2c2005400efb65527bbad4 [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
Adam Lesinskia693c4a2017-11-09 11:29:39 -080040using ::aapt::io::OutputStream;
41using ::aapt::text::Printer;
42using ::android::StringPiece;
43using ::android::base::StringPrintf;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070044
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080045namespace aapt {
46
Adam Lesinskid0f116b2016-07-08 15:00:32 -070047static const std::set<StringPiece> sJavaIdentifiers = {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070048 "abstract", "assert", "boolean", "break", "byte",
49 "case", "catch", "char", "class", "const",
50 "continue", "default", "do", "double", "else",
51 "enum", "extends", "final", "finally", "float",
52 "for", "goto", "if", "implements", "import",
53 "instanceof", "int", "interface", "long", "native",
54 "new", "package", "private", "protected", "public",
55 "return", "short", "static", "strictfp", "super",
56 "switch", "synchronized", "this", "throw", "throws",
57 "transient", "try", "void", "volatile", "while",
58 "true", "false", "null"};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080059
Adam Lesinskice5e56e2016-10-21 17:56:45 -070060static bool IsValidSymbol(const StringPiece& symbol) {
61 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080062}
63
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080064// Java symbols can not contain . or -, but those are valid in a resource name.
65// Replace those with '_'.
Adam Koskidc21dea2017-07-21 10:55:27 -070066std::string JavaClassGenerator::TransformToFieldName(const StringPiece& symbol) {
Adam Lesinskid5083f62017-01-16 15:07:21 -080067 std::string output = symbol.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 for (char& c : output) {
69 if (c == '.' || c == '-') {
70 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080071 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070072 }
73 return output;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080074}
75
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -080076// Transforms an attribute in a styleable to the Java field name:
77//
78// <declare-styleable name="Foo">
79// <attr name="android:bar" />
80// <attr name="bar" />
81// </declare-styleable>
82//
83// Foo_android_bar
84// Foo_bar
85static std::string TransformNestedAttr(const ResourceNameRef& attr_name,
86 const std::string& styleable_class_name,
87 const StringPiece& package_name_to_generate) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070088 std::string output = styleable_class_name;
Adam Lesinski74605cd2016-03-03 15:39:50 -080089
Adam Lesinskice5e56e2016-10-21 17:56:45 -070090 // We may reference IDs from other packages, so prefix the entry name with
91 // the package.
92 if (!attr_name.package.empty() &&
93 package_name_to_generate != attr_name.package) {
Adam Koskidc21dea2017-07-21 10:55:27 -070094 output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.package);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070095 }
Adam Koskidc21dea2017-07-21 10:55:27 -070096 output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.entry);
Adam Lesinskice5e56e2016-10-21 17:56:45 -070097 return output;
Adam Lesinski74605cd2016-03-03 15:39:50 -080098}
99
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800100static void AddAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700101 const uint32_t type_mask = attr->type_mask;
102 if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
103 processor->AppendComment(
104 "<p>May be a reference to another resource, in the form\n"
105 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
106 "theme\n"
107 "attribute in the form\n"
108 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
109 }
110
111 if (type_mask & android::ResTable_map::TYPE_STRING) {
112 processor->AppendComment(
113 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
114 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
115 }
116
117 if (type_mask & android::ResTable_map::TYPE_INTEGER) {
118 processor->AppendComment(
119 "<p>May be an integer value, such as \"<code>100</code>\".");
120 }
121
122 if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
123 processor->AppendComment(
124 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
125 "\"<code>false</code>\".");
126 }
127
128 if (type_mask & android::ResTable_map::TYPE_COLOR) {
129 processor->AppendComment(
130 "<p>May be a color value, in the form of "
131 "\"<code>#<i>rgb</i></code>\",\n"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800132 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code>\", or \n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700133 "\"<code>#<i>aarrggbb</i></code>\".");
134 }
135
136 if (type_mask & android::ResTable_map::TYPE_FLOAT) {
137 processor->AppendComment(
138 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
139 }
140
141 if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
142 processor->AppendComment(
143 "<p>May be a dimension value, which is a floating point number "
144 "appended with a\n"
145 "unit such as \"<code>14.5sp</code>\".\n"
146 "Available units are: px (pixels), dp (density-independent pixels),\n"
147 "sp (scaled pixels based on preferred font size), in (inches), and\n"
148 "mm (millimeters).");
149 }
150
151 if (type_mask & android::ResTable_map::TYPE_FRACTION) {
152 processor->AppendComment(
153 "<p>May be a fractional value, which is a floating point number "
154 "appended with\n"
155 "either % or %p, such as \"<code>14.5%</code>\".\n"
156 "The % suffix always means a percentage of the base size;\n"
157 "the optional %p suffix provides a size relative to some parent "
158 "container.");
159 }
160
161 if (type_mask &
162 (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
163 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
164 processor->AppendComment(
165 "<p>Must be one or more (separated by '|') of the following "
166 "constant values.</p>");
167 } else {
168 processor->AppendComment(
169 "<p>Must be one of the following constant values.</p>");
Adam Lesinski76565542016-03-10 21:55:04 -0800170 }
171
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700172 processor->AppendComment(
173 "<table>\n<colgroup align=\"left\" />\n"
174 "<colgroup align=\"left\" />\n"
175 "<colgroup align=\"left\" />\n"
176 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
177 for (const Attribute::Symbol& symbol : attr->symbols) {
178 std::stringstream line;
179 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
180 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
181 << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
182 << "</td></tr>";
183 processor->AppendComment(line.str());
Adam Lesinski76565542016-03-10 21:55:04 -0800184 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700185 processor->AppendComment("</table>");
186 }
Adam Lesinski76565542016-03-10 21:55:04 -0800187}
188
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700189JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
190 ResourceTable* table,
191 const JavaClassGeneratorOptions& options)
192 : context_(context), table_(table), options_(options) {}
193
194bool JavaClassGenerator::SkipSymbol(SymbolState state) {
195 switch (options_.types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700196 case JavaClassGeneratorOptions::SymbolTypes::kAll:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700197 return false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700198 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700199 return state == SymbolState::kUndefined;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700200 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700201 return state != SymbolState::kPublic;
202 }
203 return true;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700204}
205
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800206// Whether or not to skip writing this symbol.
207bool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
208 return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
209 !symbol.value().is_public);
210}
211
Adam Lesinski74605cd2016-03-03 15:39:50 -0800212struct StyleableAttr {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800213 const Reference* attr_ref = nullptr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700214 std::string field_name;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800215 Maybe<SymbolTable::Symbol> symbol;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800216};
217
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800218static bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
219 const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
220 const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700221 if (lhs_id < rhs_id) {
222 return true;
223 } else if (lhs_id > rhs_id) {
224 return false;
225 } else {
226 return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
227 }
228}
229
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800230void JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
231 const Styleable& styleable,
232 const StringPiece& package_name_to_generate,
233 ClassDefinition* out_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700234 MethodDefinition* out_rewrite_method,
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800235 Printer* r_txt_printer) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800236 const std::string array_field_name = TransformToFieldName(name.entry);
237 std::unique_ptr<ResourceArrayMember> array_def =
238 util::make_unique<ResourceArrayMember>(array_field_name);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700239
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800240 // The array must be sorted by resource ID.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700241 std::vector<StyleableAttr> sorted_attributes;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800242 sorted_attributes.reserve(styleable.entries.size());
243 for (const auto& attr : styleable.entries) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700244 // If we are not encoding final attributes, the styleable entry may have no
245 // ID if we are building a static library.
246 CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
247 CHECK(bool(attr.name)) << "no name set for Styleable entry";
248
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800249 // We will need the unmangled, transformed name in the comments and the field,
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700250 // so create it once and cache it in this StyleableAttr data structure.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800251 StyleableAttr styleable_attr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700252 styleable_attr.attr_ref = &attr;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700253
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800254 // The field name for this attribute is prefixed by the name of this styleable and
255 // the package it comes from.
256 styleable_attr.field_name =
257 TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700258
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800259 // Look up the symbol so that we can write out in the comments what are possible legal values
260 // for this attribute.
261 const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700262 if (symbol && symbol->attribute) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800263 // Copy the symbol data structure because the returned instance can be destroyed.
264 styleable_attr.symbol = *symbol;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700265 }
266 sorted_attributes.push_back(std::move(styleable_attr));
267 }
268
269 // Sort the attributes by ID.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800270 std::sort(sorted_attributes.begin(), sorted_attributes.end());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700271
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800272 // Build the JavaDoc comment for the Styleable array. This has references to child attributes
273 // and what possible values can be used for them.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700274 const size_t attr_count = sorted_attributes.size();
275 if (attr_count > 0) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700276 std::stringstream styleable_comment;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800277 if (!styleable.GetComment().empty()) {
278 styleable_comment << styleable.GetComment() << "\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800279 } else {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800280 // Apply a default intro comment if the styleable has no comments of its own.
281 styleable_comment << "Attributes that can be used with a " << array_field_name << ".\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800282 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700283
284 styleable_comment << "<p>Includes the following attributes:</p>\n"
285 "<table>\n"
286 "<colgroup align=\"left\" />\n"
287 "<colgroup align=\"left\" />\n"
288 "<tr><th>Attribute</th><th>Description</th></tr>\n";
289
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800290 // Build the table of attributes with their links and names.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700291 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800292 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700293 continue;
294 }
295
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800296 StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700297 if (attr_comment_line.contains("@removed")) {
298 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800299 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700300 continue;
301 }
302
303 const ResourceName& attr_name = entry.attr_ref->name.value();
Adam Lesinskie967d3f2017-07-24 18:19:36 -0700304 styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
305 << (!attr_name.package.empty() ? attr_name.package
306 : context_->GetCompilationPackage())
307 << ":" << attr_name.entry << "}</code></td>";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700308
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 Lesinskie967d3f2017-07-24 18:19:36 -0700312 styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
313 << "</td></tr>\n";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700314 }
315 styleable_comment << "</table>\n";
316
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800317 // Generate the @see lines for each attribute.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700318 for (const StyleableAttr& entry : sorted_attributes) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800319 if (SkipSymbol(entry.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700320 continue;
321 }
322 styleable_comment << "@see #" << entry.field_name << "\n";
323 }
324
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800325 array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700326 }
327
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800328 if (r_txt_printer != nullptr) {
329 r_txt_printer->Print("int[] styleable ").Print(array_field_name).Print(" {");
Adam Lesinski418763f2017-04-11 17:36:53 -0700330 }
331
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700332 // Add the ResourceIds to the array member.
Adam Lesinski418763f2017-04-11 17:36:53 -0700333 for (size_t i = 0; i < attr_count; i++) {
334 const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800335 array_def->AddElement(id);
Adam Lesinski418763f2017-04-11 17:36:53 -0700336
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800337 if (r_txt_printer != nullptr) {
Adam Lesinski418763f2017-04-11 17:36:53 -0700338 if (i != 0) {
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800339 r_txt_printer->Print(",");
Adam Lesinski418763f2017-04-11 17:36:53 -0700340 }
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800341 r_txt_printer->Print(" ").Print(id.to_string());
Adam Lesinski418763f2017-04-11 17:36:53 -0700342 }
343 }
344
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800345 if (r_txt_printer != nullptr) {
346 r_txt_printer->Println(" }");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700347 }
348
349 // Add the Styleable array to the Styleable class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800350 out_class_def->AddMember(std::move(array_def));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700351
352 // Now we emit the indices into the array.
353 for (size_t i = 0; i < attr_count; i++) {
354 const StyleableAttr& styleable_attr = sorted_attributes[i];
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800355 if (SkipSymbol(styleable_attr.symbol)) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700356 continue;
357 }
358
359 StringPiece comment = styleable_attr.attr_ref->GetComment();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800360 if (styleable_attr.symbol.value().attribute && comment.empty()) {
361 comment = styleable_attr.symbol.value().attribute->GetComment();
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700362 }
363
364 if (comment.contains("@removed")) {
365 // Removed attributes are public but hidden from the documentation, so
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800366 // don't emit them as part of the class documentation.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700367 continue;
368 }
369
370 const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
371
372 StringPiece package_name = attr_name.package;
373 if (package_name.empty()) {
374 package_name = context_->GetCompilationPackage();
375 }
376
377 std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
378 sorted_attributes[i].field_name, static_cast<uint32_t>(i));
379
380 AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
381
382 if (!comment.empty()) {
383 attr_processor->AppendComment("<p>\n@attr description");
384 attr_processor->AppendComment(comment);
385 } else {
386 std::stringstream default_comment;
387 default_comment << "<p>This symbol is the offset where the "
388 << "{@link " << package_name << ".R.attr#"
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800389 << TransformToFieldName(attr_name.entry) << "}\n"
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700390 << "attribute's value can be found in the "
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800391 << "{@link #" << array_field_name << "} array.";
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700392 attr_processor->AppendComment(default_comment.str());
393 }
394
395 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800396 AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700397 attr_processor->AppendNewLine();
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800398 attr_processor->AppendComment(
399 StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700400
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800401 if (r_txt_printer != nullptr) {
402 r_txt_printer->Println(
403 StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
Adam Lesinski418763f2017-04-11 17:36:53 -0700404 }
405
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800406 out_class_def->AddMember(std::move(index_member));
407 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700408
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800409 // If there is a rewrite method to generate, add the statements that rewrite package IDs
410 // for this styleable.
411 if (out_rewrite_method != nullptr) {
412 out_rewrite_method->AppendStatement(
413 StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
414 out_rewrite_method->AppendStatement(
415 StringPrintf(" if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
416 out_rewrite_method->AppendStatement(
417 StringPrintf(" styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (p << 24);",
418 array_field_name.data(), array_field_name.data()));
419 out_rewrite_method->AppendStatement(" }");
420 out_rewrite_method->AppendStatement("}");
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700421 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800422}
423
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800424void JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
425 const ResourceEntry& entry, ClassDefinition* out_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700426 MethodDefinition* out_rewrite_method,
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800427 text::Printer* r_txt_printer) {
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700428 ResourceId real_id = id;
429 if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
430 id.package_id() > kAppPackageId) {
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800431 // Workaround for feature splits using package IDs > 0x7F.
432 // See b/37498913.
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700433 real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
434 }
435
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800436 const std::string field_name = TransformToFieldName(name.entry);
437 std::unique_ptr<ResourceMember> resource_member =
Adam Lesinski1e4b0e52017-04-27 15:01:10 -0700438 util::make_unique<ResourceMember>(field_name, real_id);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800439
440 // Build the comments and annotations for this entry.
441 AnnotationProcessor* processor = resource_member->GetCommentBuilder();
442
443 // Add the comments from any <public> tags.
444 if (entry.symbol_status.state != SymbolState::kUndefined) {
445 processor->AppendComment(entry.symbol_status.comment);
446 }
447
448 // Add the comments from all configurations of this entry.
449 for (const auto& config_value : entry.values) {
450 processor->AppendComment(config_value->value->GetComment());
451 }
452
453 // If this is an Attribute, append the format Javadoc.
454 if (!entry.values.empty()) {
455 if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
456 // We list out the available values for the given attribute.
457 AddAttributeFormatDoc(processor, attr);
458 }
459 }
460
461 out_class_def->AddMember(std::move(resource_member));
462
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800463 if (r_txt_printer != nullptr) {
464 r_txt_printer->Print("int ")
465 .Print(to_string(name.type))
466 .Print(" ")
467 .Print(field_name)
468 .Print(" ")
469 .Println(real_id.to_string());
Adam Lesinski418763f2017-04-11 17:36:53 -0700470 }
471
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800472 if (out_rewrite_method != nullptr) {
Adam Lesinski93190b72017-11-03 15:20:17 -0700473 const StringPiece& type_str = to_string(name.type);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800474 out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
475 type_str.data(), field_name.data(),
476 type_str.data(), field_name.data()));
477 }
478}
479
480Maybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
481 const StringPiece& package_name_to_generate,
482 const ResourceEntry& entry) {
483 if (SkipSymbol(entry.symbol_status.state)) {
484 return {};
485 }
486
487 std::string unmangled_package;
488 std::string unmangled_name = entry.name;
489 if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
490 // The entry name was mangled, and we successfully unmangled it.
491 // Check that we want to emit this symbol.
Adam Lesinski1ef0fa92017-08-15 21:32:49 -0700492 if (package_name_to_generate != unmangled_package) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800493 // Skip the entry if it doesn't belong to the package we're writing.
494 return {};
495 }
496 } else if (package_name_to_generate != package_name) {
497 // We are processing a mangled package name,
498 // but this is a non-mangled resource.
499 return {};
500 }
501 return {std::move(unmangled_name)};
502}
503
504bool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate,
505 const ResourceTablePackage& package,
506 const ResourceTableType& type,
507 ClassDefinition* out_type_class_def,
Adam Lesinski418763f2017-04-11 17:36:53 -0700508 MethodDefinition* out_rewrite_method_def,
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800509 Printer* r_txt_printer) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800510 for (const auto& entry : type.entries) {
511 const Maybe<std::string> unmangled_name =
512 UnmangleResource(package.name, package_name_to_generate, *entry);
513 if (!unmangled_name) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700514 continue;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800515 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700516
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800517 // Create an ID if there is one (static libraries don't need one).
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700518 ResourceId id;
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800519 if (package.id && type.id && entry->id) {
520 id = ResourceId(package.id.value(), type.id.value(), entry->id.value());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800521 }
522
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800523 // We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
524 const ResourceNameRef resource_name(
525 package_name_to_generate,
526 type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type,
527 unmangled_name.value());
Adam Lesinskib274e352015-11-06 15:14:35 -0800528
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800529 // Check to see if the unmangled name is a valid Java name (not a keyword).
530 if (!IsValidSymbol(unmangled_name.value())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700531 std::stringstream err;
532 err << "invalid symbol name '" << resource_name << "'";
533 error_ = err.str();
534 return false;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700535 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700536
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800537 if (resource_name.type == ResourceType::kStyleable) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700538 CHECK(!entry->values.empty());
539
540 const Styleable* styleable =
541 static_cast<const Styleable*>(entry->values.front()->value.get());
542
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800543 ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800544 out_rewrite_method_def, r_txt_printer);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700545 } else {
Adam Lesinski418763f2017-04-11 17:36:53 -0700546 ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800547 r_txt_printer);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700548 }
549 }
550 return true;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700551}
552
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800553bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, OutputStream* out,
554 OutputStream* out_r_txt) {
555 return Generate(package_name_to_generate, package_name_to_generate, out, out_r_txt);
Adam Lesinski769de982015-04-10 19:43:55 -0700556}
557
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800558static void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
559 AnnotationProcessor* processor) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700560 for (const std::string& annotation : annotations) {
561 std::string proper_annotation = "@";
562 proper_annotation += annotation;
563 processor->AppendComment(proper_annotation);
564 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700565}
566
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700567bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800568 const StringPiece& out_package_name, OutputStream* out,
569 OutputStream* out_r_txt) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800570 ClassDefinition r_class("R", ClassQualifier::kNone, true);
571 std::unique_ptr<MethodDefinition> rewrite_method;
572
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800573 std::unique_ptr<Printer> r_txt_printer;
574 if (out_r_txt != nullptr) {
575 r_txt_printer = util::make_unique<Printer>(out_r_txt);
576 }
577
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800578 // Generate an onResourcesLoaded() callback if requested.
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800579 if (options_.rewrite_callback_options) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800580 rewrite_method =
581 util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
Adam Lesinskib5dc4bd2017-02-22 19:29:29 -0800582 for (const std::string& package_to_callback :
583 options_.rewrite_callback_options.value().packages_to_callback) {
584 rewrite_method->AppendStatement(
585 StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
586 }
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800587 }
Adam Lesinski3524a232016-04-01 19:19:24 -0700588
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700589 for (const auto& package : table_->packages) {
590 for (const auto& type : package->types) {
591 if (type->type == ResourceType::kAttrPrivate) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800592 // We generate these as part of the kAttr type, so skip them here.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700593 continue;
594 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800595
Adam Lesinski1ef0fa92017-08-15 21:32:49 -0700596 // Stay consistent with AAPT and generate an empty type class if the R class is public.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700597 const bool force_creation_if_empty =
598 (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800599
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800600 std::unique_ptr<ClassDefinition> class_def = util::make_unique<ClassDefinition>(
Adam Lesinski93190b72017-11-03 15:20:17 -0700601 to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800602 if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800603 rewrite_method.get(), r_txt_printer.get())) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700604 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700605 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700606
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700607 if (type->type == ResourceType::kAttr) {
608 // Also include private attributes in this same class.
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800609 const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700610 if (priv_type) {
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800611 if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800612 rewrite_method.get(), r_txt_printer.get())) {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700613 return false;
614 }
615 }
616 }
617
618 if (type->type == ResourceType::kStyleable &&
619 options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
620 // When generating a public R class, we don't want Styleable to be part
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800621 // of the API. It is only emitted for documentation purposes.
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700622 class_def->GetCommentBuilder()->AppendComment("@doconly");
623 }
624
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800625 AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700626
627 r_class.AddMember(std::move(class_def));
628 }
629 }
630
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800631 if (rewrite_method != nullptr) {
632 r_class.AddMember(std::move(rewrite_method));
633 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700634
Adam Lesinskiceb9b2f2017-02-16 12:05:42 -0800635 AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
Adam Lesinskia693c4a2017-11-09 11:29:39 -0800636 ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700637 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800638}
639
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700640} // namespace aapt