blob: b71dc485633b4d129f2e3ce5e06d5c842cf7bd29 [file] [log] [blame]
Adam Lesinski6f6ceb72014-11-14 14:48:12 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Adam Lesinskica5638f2015-10-21 14:42:43 -070017#include "java/JavaClassGenerator.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018
Adam Lesinskica2fc352015-04-03 12:08:26 -070019#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020#include <ostream>
21#include <set>
22#include <sstream>
23#include <tuple>
24
Adam Lesinskice5e56e2016-10-21 17:56:45 -070025#include "android-base/logging.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080026#include "androidfw/StringPiece.h"
Adam Lesinskice5e56e2016-10-21 17:56:45 -070027
28#include "NameMangler.h"
29#include "Resource.h"
30#include "ResourceTable.h"
31#include "ResourceValues.h"
32#include "ValueVisitor.h"
33#include "java/AnnotationProcessor.h"
34#include "java/ClassDefinition.h"
35#include "process/SymbolTable.h"
Adam Lesinskid5083f62017-01-16 15:07:21 -080036
37using android::StringPiece;
Adam Lesinskice5e56e2016-10-21 17:56:45 -070038
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080039namespace aapt {
40
Adam Lesinskid0f116b2016-07-08 15:00:32 -070041static const std::set<StringPiece> sJavaIdentifiers = {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070042 "abstract", "assert", "boolean", "break", "byte",
43 "case", "catch", "char", "class", "const",
44 "continue", "default", "do", "double", "else",
45 "enum", "extends", "final", "finally", "float",
46 "for", "goto", "if", "implements", "import",
47 "instanceof", "int", "interface", "long", "native",
48 "new", "package", "private", "protected", "public",
49 "return", "short", "static", "strictfp", "super",
50 "switch", "synchronized", "this", "throw", "throws",
51 "transient", "try", "void", "volatile", "while",
52 "true", "false", "null"};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080053
Adam Lesinskice5e56e2016-10-21 17:56:45 -070054static bool IsValidSymbol(const StringPiece& symbol) {
55 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080056}
57
58/*
59 * Java symbols can not contain . or -, but those are valid in a resource name.
60 * Replace those with '_'.
61 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070062static std::string Transform(const StringPiece& symbol) {
Adam Lesinskid5083f62017-01-16 15:07:21 -080063 std::string output = symbol.to_string();
Adam Lesinskice5e56e2016-10-21 17:56:45 -070064 for (char& c : output) {
65 if (c == '.' || c == '-') {
66 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070068 }
69 return output;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080070}
71
Adam Lesinski74605cd2016-03-03 15:39:50 -080072/**
73 * Transforms an attribute in a styleable to the Java field name:
74 *
75 * <declare-styleable name="Foo">
76 * <attr name="android:bar" />
77 * <attr name="bar" />
78 * </declare-styleable>
79 *
80 * Foo_android_bar
81 * Foo_bar
82 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070083static std::string TransformNestedAttr(
84 const ResourceNameRef& attr_name, const std::string& styleable_class_name,
85 const StringPiece& package_name_to_generate) {
86 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) {
92 output += "_" + Transform(attr_name.package);
93 }
94 output += "_" + Transform(attr_name.entry);
95 return output;
Adam Lesinski74605cd2016-03-03 15:39:50 -080096}
97
Adam Lesinskice5e56e2016-10-21 17:56:45 -070098static void AddAttributeFormatDoc(AnnotationProcessor* processor,
99 Attribute* attr) {
100 const uint32_t type_mask = attr->type_mask;
101 if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
102 processor->AppendComment(
103 "<p>May be a reference to another resource, in the form\n"
104 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
105 "theme\n"
106 "attribute in the form\n"
107 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
108 }
109
110 if (type_mask & android::ResTable_map::TYPE_STRING) {
111 processor->AppendComment(
112 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
113 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
114 }
115
116 if (type_mask & android::ResTable_map::TYPE_INTEGER) {
117 processor->AppendComment(
118 "<p>May be an integer value, such as \"<code>100</code>\".");
119 }
120
121 if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
122 processor->AppendComment(
123 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
124 "\"<code>false</code>\".");
125 }
126
127 if (type_mask & android::ResTable_map::TYPE_COLOR) {
128 processor->AppendComment(
129 "<p>May be a color value, in the form of "
130 "\"<code>#<i>rgb</i></code>\",\n"
131 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
132 "\"<code>#<i>aarrggbb</i></code>\".");
133 }
134
135 if (type_mask & android::ResTable_map::TYPE_FLOAT) {
136 processor->AppendComment(
137 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
138 }
139
140 if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
141 processor->AppendComment(
142 "<p>May be a dimension value, which is a floating point number "
143 "appended with a\n"
144 "unit such as \"<code>14.5sp</code>\".\n"
145 "Available units are: px (pixels), dp (density-independent pixels),\n"
146 "sp (scaled pixels based on preferred font size), in (inches), and\n"
147 "mm (millimeters).");
148 }
149
150 if (type_mask & android::ResTable_map::TYPE_FRACTION) {
151 processor->AppendComment(
152 "<p>May be a fractional value, which is a floating point number "
153 "appended with\n"
154 "either % or %p, such as \"<code>14.5%</code>\".\n"
155 "The % suffix always means a percentage of the base size;\n"
156 "the optional %p suffix provides a size relative to some parent "
157 "container.");
158 }
159
160 if (type_mask &
161 (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
162 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
163 processor->AppendComment(
164 "<p>Must be one or more (separated by '|') of the following "
165 "constant values.</p>");
166 } else {
167 processor->AppendComment(
168 "<p>Must be one of the following constant values.</p>");
Adam Lesinski76565542016-03-10 21:55:04 -0800169 }
170
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700171 processor->AppendComment(
172 "<table>\n<colgroup align=\"left\" />\n"
173 "<colgroup align=\"left\" />\n"
174 "<colgroup align=\"left\" />\n"
175 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
176 for (const Attribute::Symbol& symbol : attr->symbols) {
177 std::stringstream line;
178 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
179 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
180 << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
181 << "</td></tr>";
182 processor->AppendComment(line.str());
Adam Lesinski76565542016-03-10 21:55:04 -0800183 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700184 processor->AppendComment("</table>");
185 }
Adam Lesinski76565542016-03-10 21:55:04 -0800186}
187
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700188JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
189 ResourceTable* table,
190 const JavaClassGeneratorOptions& options)
191 : context_(context), table_(table), options_(options) {}
192
193bool JavaClassGenerator::SkipSymbol(SymbolState state) {
194 switch (options_.types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700195 case JavaClassGeneratorOptions::SymbolTypes::kAll:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700196 return false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700197 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700198 return state == SymbolState::kUndefined;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700199 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700200 return state != SymbolState::kPublic;
201 }
202 return true;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700203}
204
Adam Lesinski74605cd2016-03-03 15:39:50 -0800205struct StyleableAttr {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700206 const Reference* attr_ref;
207 std::string field_name;
208 std::unique_ptr<SymbolTable::Symbol> symbol;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800209};
210
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700211static bool less_styleable_attr(const StyleableAttr& lhs,
212 const StyleableAttr& rhs) {
213 const ResourceId lhs_id =
214 lhs.attr_ref->id ? lhs.attr_ref->id.value() : ResourceId(0);
215 const ResourceId rhs_id =
216 rhs.attr_ref->id ? rhs.attr_ref->id.value() : ResourceId(0);
217 if (lhs_id < rhs_id) {
218 return true;
219 } else if (lhs_id > rhs_id) {
220 return false;
221 } else {
222 return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
223 }
224}
225
226void JavaClassGenerator::AddMembersToStyleableClass(
227 const StringPiece& package_name_to_generate, const std::string& entry_name,
228 const Styleable* styleable, ClassDefinition* out_styleable_class_def) {
229 const std::string class_name = Transform(entry_name);
230
231 std::unique_ptr<ResourceArrayMember> styleable_array_def =
232 util::make_unique<ResourceArrayMember>(class_name);
233
234 // This must be sorted by resource ID.
235 std::vector<StyleableAttr> sorted_attributes;
236 sorted_attributes.reserve(styleable->entries.size());
237 for (const auto& attr : styleable->entries) {
238 // If we are not encoding final attributes, the styleable entry may have no
239 // ID if we are building a static library.
240 CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
241 CHECK(bool(attr.name)) << "no name set for Styleable entry";
242
243 // We will need the unmangled, transformed name in the comments and the
244 // field,
245 // so create it once and cache it in this StyleableAttr data structure.
246 StyleableAttr styleable_attr = {};
247 styleable_attr.attr_ref = &attr;
248 styleable_attr.field_name = TransformNestedAttr(
249 attr.name.value(), class_name, package_name_to_generate);
250
251 Reference mangled_reference;
252 mangled_reference.id = attr.id;
253 mangled_reference.name = attr.name;
254 if (mangled_reference.name.value().package.empty()) {
255 mangled_reference.name.value().package =
256 context_->GetCompilationPackage();
257 }
258
259 if (Maybe<ResourceName> mangled_name =
260 context_->GetNameMangler()->MangleName(
261 mangled_reference.name.value())) {
262 mangled_reference.name = mangled_name;
263 }
264
265 // Look up the symbol so that we can write out in the comments what are
266 // possible
267 // legal values for this attribute.
268 const SymbolTable::Symbol* symbol =
269 context_->GetExternalSymbols()->FindByReference(mangled_reference);
270 if (symbol && symbol->attribute) {
271 // Copy the symbol data structure because the returned instance can be
272 // destroyed.
273 styleable_attr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
274 }
275 sorted_attributes.push_back(std::move(styleable_attr));
276 }
277
278 // Sort the attributes by ID.
279 std::sort(sorted_attributes.begin(), sorted_attributes.end(),
280 less_styleable_attr);
281
282 const size_t attr_count = sorted_attributes.size();
283 if (attr_count > 0) {
284 // Build the comment string for the Styleable. It includes details about the
285 // child attributes.
286 std::stringstream styleable_comment;
287 if (!styleable->GetComment().empty()) {
288 styleable_comment << styleable->GetComment() << "\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800289 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700290 styleable_comment << "Attributes that can be used with a " << class_name
291 << ".\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800292 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700293
294 styleable_comment << "<p>Includes the following attributes:</p>\n"
295 "<table>\n"
296 "<colgroup align=\"left\" />\n"
297 "<colgroup align=\"left\" />\n"
298 "<tr><th>Attribute</th><th>Description</th></tr>\n";
299
300 for (const StyleableAttr& entry : sorted_attributes) {
301 if (!entry.symbol) {
302 continue;
303 }
304
305 if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
306 !entry.symbol->is_public) {
307 // Don't write entries for non-public attributes.
308 continue;
309 }
310
311 StringPiece attr_comment_line = entry.symbol->attribute->GetComment();
312 if (attr_comment_line.contains("@removed")) {
313 // Removed attributes are public but hidden from the documentation, so
314 // don't emit
315 // them as part of the class documentation.
316 continue;
317 }
318
319 const ResourceName& attr_name = entry.attr_ref->name.value();
320 styleable_comment << "<tr><td>";
321 styleable_comment << "<code>{@link #" << entry.field_name << " "
322 << (!attr_name.package.empty()
323 ? attr_name.package
324 : context_->GetCompilationPackage())
325 << ":" << attr_name.entry << "}</code>";
326 styleable_comment << "</td>";
327
328 styleable_comment << "<td>";
329
330 // Only use the comment up until the first '.'. This is to stay compatible
331 // with
332 // the way old AAPT did it (presumably to keep it short and to avoid
333 // including
334 // annotations like @hide which would affect this Styleable).
335 auto iter =
336 std::find(attr_comment_line.begin(), attr_comment_line.end(), u'.');
337 if (iter != attr_comment_line.end()) {
338 attr_comment_line =
339 attr_comment_line.substr(0, (iter - attr_comment_line.begin()) + 1);
340 }
341 styleable_comment << attr_comment_line << "</td></tr>\n";
342 }
343 styleable_comment << "</table>\n";
344
345 for (const StyleableAttr& entry : sorted_attributes) {
346 if (!entry.symbol) {
347 continue;
348 }
349
350 if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
351 !entry.symbol->is_public) {
352 // Don't write entries for non-public attributes.
353 continue;
354 }
355 styleable_comment << "@see #" << entry.field_name << "\n";
356 }
357
358 styleable_array_def->GetCommentBuilder()->AppendComment(
359 styleable_comment.str());
360 }
361
362 // Add the ResourceIds to the array member.
363 for (const StyleableAttr& styleable_attr : sorted_attributes) {
364 styleable_array_def->AddElement(styleable_attr.attr_ref->id
365 ? styleable_attr.attr_ref->id.value()
366 : ResourceId(0));
367 }
368
369 // Add the Styleable array to the Styleable class.
370 out_styleable_class_def->AddMember(std::move(styleable_array_def));
371
372 // Now we emit the indices into the array.
373 for (size_t i = 0; i < attr_count; i++) {
374 const StyleableAttr& styleable_attr = sorted_attributes[i];
375
376 if (!styleable_attr.symbol) {
377 continue;
378 }
379
380 if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
381 !styleable_attr.symbol->is_public) {
382 // Don't write entries for non-public attributes.
383 continue;
384 }
385
386 StringPiece comment = styleable_attr.attr_ref->GetComment();
387 if (styleable_attr.symbol->attribute && comment.empty()) {
388 comment = styleable_attr.symbol->attribute->GetComment();
389 }
390
391 if (comment.contains("@removed")) {
392 // Removed attributes are public but hidden from the documentation, so
393 // don't emit them
394 // as part of the class documentation.
395 continue;
396 }
397
398 const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
399
400 StringPiece package_name = attr_name.package;
401 if (package_name.empty()) {
402 package_name = context_->GetCompilationPackage();
403 }
404
405 std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
406 sorted_attributes[i].field_name, static_cast<uint32_t>(i));
407
408 AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
409
410 if (!comment.empty()) {
411 attr_processor->AppendComment("<p>\n@attr description");
412 attr_processor->AppendComment(comment);
413 } else {
414 std::stringstream default_comment;
415 default_comment << "<p>This symbol is the offset where the "
416 << "{@link " << package_name << ".R.attr#"
417 << Transform(attr_name.entry) << "}\n"
418 << "attribute's value can be found in the "
419 << "{@link #" << class_name << "} array.";
420 attr_processor->AppendComment(default_comment.str());
421 }
422
423 attr_processor->AppendNewLine();
424
425 AddAttributeFormatDoc(attr_processor,
426 styleable_attr.symbol->attribute.get());
427 attr_processor->AppendNewLine();
428
429 std::stringstream doclava_name;
430 doclava_name << "@attr name " << package_name << ":" << attr_name.entry;
431
432 attr_processor->AppendComment(doclava_name.str());
433
434 out_styleable_class_def->AddMember(std::move(index_member));
435 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800436}
437
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700438bool JavaClassGenerator::AddMembersToTypeClass(
439 const StringPiece& package_name_to_generate,
440 const ResourceTablePackage* package, const ResourceTableType* type,
441 ClassDefinition* out_type_class_def) {
442 for (const auto& entry : type->entries) {
443 if (SkipSymbol(entry->symbol_status.state)) {
444 continue;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800445 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700446
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700447 ResourceId id;
448 if (package->id && type->id && entry->id) {
449 id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800450 }
451
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700452 std::string unmangled_package;
453 std::string unmangled_name = entry->name;
454 if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
455 // The entry name was mangled, and we successfully unmangled it.
456 // Check that we want to emit this symbol.
457 if (package->name != unmangled_package) {
458 // Skip the entry if it doesn't belong to the package we're writing.
459 continue;
460 }
461 } else if (package_name_to_generate != package->name) {
462 // We are processing a mangled package name,
463 // but this is a non-mangled resource.
464 continue;
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700465 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800466
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700467 if (!IsValidSymbol(unmangled_name)) {
468 ResourceNameRef resource_name(package_name_to_generate, type->type,
469 unmangled_name);
470 std::stringstream err;
471 err << "invalid symbol name '" << resource_name << "'";
472 error_ = err.str();
473 return false;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700474 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700475
476 if (type->type == ResourceType::kStyleable) {
477 CHECK(!entry->values.empty());
478
479 const Styleable* styleable =
480 static_cast<const Styleable*>(entry->values.front()->value.get());
481
482 // Comments are handled within this method.
483 AddMembersToStyleableClass(package_name_to_generate, unmangled_name,
484 styleable, out_type_class_def);
485 } else {
486 std::unique_ptr<ResourceMember> resource_member =
487 util::make_unique<ResourceMember>(Transform(unmangled_name), id);
488
489 // Build the comments and annotations for this entry.
490 AnnotationProcessor* processor = resource_member->GetCommentBuilder();
491
492 // Add the comments from any <public> tags.
493 if (entry->symbol_status.state != SymbolState::kUndefined) {
494 processor->AppendComment(entry->symbol_status.comment);
495 }
496
497 // Add the comments from all configurations of this entry.
498 for (const auto& config_value : entry->values) {
499 processor->AppendComment(config_value->value->GetComment());
500 }
501
502 // If this is an Attribute, append the format Javadoc.
503 if (!entry->values.empty()) {
504 if (Attribute* attr =
505 ValueCast<Attribute>(entry->values.front()->value.get())) {
506 // We list out the available values for the given attribute.
507 AddAttributeFormatDoc(processor, attr);
508 }
509 }
510
511 out_type_class_def->AddMember(std::move(resource_member));
512 }
513 }
514 return true;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700515}
516
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700517bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
518 std::ostream* out) {
519 return Generate(package_name_to_generate, package_name_to_generate, out);
Adam Lesinski769de982015-04-10 19:43:55 -0700520}
521
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700522static void AppendJavaDocAnnotations(
523 const std::vector<std::string>& annotations,
524 AnnotationProcessor* processor) {
525 for (const std::string& annotation : annotations) {
526 std::string proper_annotation = "@";
527 proper_annotation += annotation;
528 processor->AppendComment(proper_annotation);
529 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700530}
531
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700532bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
533 const StringPiece& out_package_name,
534 std::ostream* out) {
535 ClassDefinition r_class("R", ClassQualifier::None, true);
Adam Lesinski3524a232016-04-01 19:19:24 -0700536
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700537 for (const auto& package : table_->packages) {
538 for (const auto& type : package->types) {
539 if (type->type == ResourceType::kAttrPrivate) {
540 continue;
541 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800542
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700543 const bool force_creation_if_empty =
544 (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800545
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700546 std::unique_ptr<ClassDefinition> class_def =
547 util::make_unique<ClassDefinition>(ToString(type->type),
548 ClassQualifier::Static,
549 force_creation_if_empty);
Adam Lesinskib274e352015-11-06 15:14:35 -0800550
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700551 bool result = AddMembersToTypeClass(
552 package_name_to_generate, package.get(), type.get(), class_def.get());
553 if (!result) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700554 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700555 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700556
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700557 if (type->type == ResourceType::kAttr) {
558 // Also include private attributes in this same class.
559 ResourceTableType* priv_type =
560 package->FindType(ResourceType::kAttrPrivate);
561 if (priv_type) {
562 result =
563 AddMembersToTypeClass(package_name_to_generate, package.get(),
564 priv_type, class_def.get());
565 if (!result) {
566 return false;
567 }
568 }
569 }
570
571 if (type->type == ResourceType::kStyleable &&
572 options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
573 // When generating a public R class, we don't want Styleable to be part
574 // of the API.
575 // It is only emitted for documentation purposes.
576 class_def->GetCommentBuilder()->AppendComment("@doconly");
577 }
578
579 AppendJavaDocAnnotations(options_.javadoc_annotations,
580 class_def->GetCommentBuilder());
581
582 r_class.AddMember(std::move(class_def));
583 }
584 }
585
586 AppendJavaDocAnnotations(options_.javadoc_annotations,
587 r_class.GetCommentBuilder());
588
589 if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name,
590 options_.use_final, out)) {
591 return false;
592 }
593
594 out->flush();
595 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800596}
597
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700598} // namespace aapt