blob: 6e7c707847b9614b507511a068029dfdad80ee85 [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"
26
27#include "NameMangler.h"
28#include "Resource.h"
29#include "ResourceTable.h"
30#include "ResourceValues.h"
31#include "ValueVisitor.h"
32#include "java/AnnotationProcessor.h"
33#include "java/ClassDefinition.h"
34#include "process/SymbolTable.h"
35#include "util/StringPiece.h"
36
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080037namespace aapt {
38
Adam Lesinskid0f116b2016-07-08 15:00:32 -070039static const std::set<StringPiece> sJavaIdentifiers = {
Adam Lesinskice5e56e2016-10-21 17:56:45 -070040 "abstract", "assert", "boolean", "break", "byte",
41 "case", "catch", "char", "class", "const",
42 "continue", "default", "do", "double", "else",
43 "enum", "extends", "final", "finally", "float",
44 "for", "goto", "if", "implements", "import",
45 "instanceof", "int", "interface", "long", "native",
46 "new", "package", "private", "protected", "public",
47 "return", "short", "static", "strictfp", "super",
48 "switch", "synchronized", "this", "throw", "throws",
49 "transient", "try", "void", "volatile", "while",
50 "true", "false", "null"};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080051
Adam Lesinskice5e56e2016-10-21 17:56:45 -070052static bool IsValidSymbol(const StringPiece& symbol) {
53 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080054}
55
56/*
57 * Java symbols can not contain . or -, but those are valid in a resource name.
58 * Replace those with '_'.
59 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070060static std::string Transform(const StringPiece& symbol) {
61 std::string output = symbol.ToString();
62 for (char& c : output) {
63 if (c == '.' || c == '-') {
64 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080065 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -070066 }
67 return output;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080068}
69
Adam Lesinski74605cd2016-03-03 15:39:50 -080070/**
71 * Transforms an attribute in a styleable to the Java field name:
72 *
73 * <declare-styleable name="Foo">
74 * <attr name="android:bar" />
75 * <attr name="bar" />
76 * </declare-styleable>
77 *
78 * Foo_android_bar
79 * Foo_bar
80 */
Adam Lesinskice5e56e2016-10-21 17:56:45 -070081static std::string TransformNestedAttr(
82 const ResourceNameRef& attr_name, const std::string& styleable_class_name,
83 const StringPiece& package_name_to_generate) {
84 std::string output = styleable_class_name;
Adam Lesinski74605cd2016-03-03 15:39:50 -080085
Adam Lesinskice5e56e2016-10-21 17:56:45 -070086 // We may reference IDs from other packages, so prefix the entry name with
87 // the package.
88 if (!attr_name.package.empty() &&
89 package_name_to_generate != attr_name.package) {
90 output += "_" + Transform(attr_name.package);
91 }
92 output += "_" + Transform(attr_name.entry);
93 return output;
Adam Lesinski74605cd2016-03-03 15:39:50 -080094}
95
Adam Lesinskice5e56e2016-10-21 17:56:45 -070096static void AddAttributeFormatDoc(AnnotationProcessor* processor,
97 Attribute* attr) {
98 const uint32_t type_mask = attr->type_mask;
99 if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
100 processor->AppendComment(
101 "<p>May be a reference to another resource, in the form\n"
102 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
103 "theme\n"
104 "attribute in the form\n"
105 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
106 }
107
108 if (type_mask & android::ResTable_map::TYPE_STRING) {
109 processor->AppendComment(
110 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
111 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
112 }
113
114 if (type_mask & android::ResTable_map::TYPE_INTEGER) {
115 processor->AppendComment(
116 "<p>May be an integer value, such as \"<code>100</code>\".");
117 }
118
119 if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
120 processor->AppendComment(
121 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
122 "\"<code>false</code>\".");
123 }
124
125 if (type_mask & android::ResTable_map::TYPE_COLOR) {
126 processor->AppendComment(
127 "<p>May be a color value, in the form of "
128 "\"<code>#<i>rgb</i></code>\",\n"
129 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
130 "\"<code>#<i>aarrggbb</i></code>\".");
131 }
132
133 if (type_mask & android::ResTable_map::TYPE_FLOAT) {
134 processor->AppendComment(
135 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
136 }
137
138 if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
139 processor->AppendComment(
140 "<p>May be a dimension value, which is a floating point number "
141 "appended with a\n"
142 "unit such as \"<code>14.5sp</code>\".\n"
143 "Available units are: px (pixels), dp (density-independent pixels),\n"
144 "sp (scaled pixels based on preferred font size), in (inches), and\n"
145 "mm (millimeters).");
146 }
147
148 if (type_mask & android::ResTable_map::TYPE_FRACTION) {
149 processor->AppendComment(
150 "<p>May be a fractional value, which is a floating point number "
151 "appended with\n"
152 "either % or %p, such as \"<code>14.5%</code>\".\n"
153 "The % suffix always means a percentage of the base size;\n"
154 "the optional %p suffix provides a size relative to some parent "
155 "container.");
156 }
157
158 if (type_mask &
159 (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
160 if (type_mask & android::ResTable_map::TYPE_FLAGS) {
161 processor->AppendComment(
162 "<p>Must be one or more (separated by '|') of the following "
163 "constant values.</p>");
164 } else {
165 processor->AppendComment(
166 "<p>Must be one of the following constant values.</p>");
Adam Lesinski76565542016-03-10 21:55:04 -0800167 }
168
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700169 processor->AppendComment(
170 "<table>\n<colgroup align=\"left\" />\n"
171 "<colgroup align=\"left\" />\n"
172 "<colgroup align=\"left\" />\n"
173 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
174 for (const Attribute::Symbol& symbol : attr->symbols) {
175 std::stringstream line;
176 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
177 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
178 << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
179 << "</td></tr>";
180 processor->AppendComment(line.str());
Adam Lesinski76565542016-03-10 21:55:04 -0800181 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700182 processor->AppendComment("</table>");
183 }
Adam Lesinski76565542016-03-10 21:55:04 -0800184}
185
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700186JavaClassGenerator::JavaClassGenerator(IAaptContext* context,
187 ResourceTable* table,
188 const JavaClassGeneratorOptions& options)
189 : context_(context), table_(table), options_(options) {}
190
191bool JavaClassGenerator::SkipSymbol(SymbolState state) {
192 switch (options_.types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700193 case JavaClassGeneratorOptions::SymbolTypes::kAll:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700194 return false;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700195 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700196 return state == SymbolState::kUndefined;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700197 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700198 return state != SymbolState::kPublic;
199 }
200 return true;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700201}
202
Adam Lesinski74605cd2016-03-03 15:39:50 -0800203struct StyleableAttr {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700204 const Reference* attr_ref;
205 std::string field_name;
206 std::unique_ptr<SymbolTable::Symbol> symbol;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800207};
208
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700209static bool less_styleable_attr(const StyleableAttr& lhs,
210 const StyleableAttr& rhs) {
211 const ResourceId lhs_id =
212 lhs.attr_ref->id ? lhs.attr_ref->id.value() : ResourceId(0);
213 const ResourceId rhs_id =
214 rhs.attr_ref->id ? rhs.attr_ref->id.value() : ResourceId(0);
215 if (lhs_id < rhs_id) {
216 return true;
217 } else if (lhs_id > rhs_id) {
218 return false;
219 } else {
220 return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
221 }
222}
223
224void JavaClassGenerator::AddMembersToStyleableClass(
225 const StringPiece& package_name_to_generate, const std::string& entry_name,
226 const Styleable* styleable, ClassDefinition* out_styleable_class_def) {
227 const std::string class_name = Transform(entry_name);
228
229 std::unique_ptr<ResourceArrayMember> styleable_array_def =
230 util::make_unique<ResourceArrayMember>(class_name);
231
232 // This must be sorted by resource ID.
233 std::vector<StyleableAttr> sorted_attributes;
234 sorted_attributes.reserve(styleable->entries.size());
235 for (const auto& attr : styleable->entries) {
236 // If we are not encoding final attributes, the styleable entry may have no
237 // ID if we are building a static library.
238 CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
239 CHECK(bool(attr.name)) << "no name set for Styleable entry";
240
241 // We will need the unmangled, transformed name in the comments and the
242 // field,
243 // so create it once and cache it in this StyleableAttr data structure.
244 StyleableAttr styleable_attr = {};
245 styleable_attr.attr_ref = &attr;
246 styleable_attr.field_name = TransformNestedAttr(
247 attr.name.value(), class_name, package_name_to_generate);
248
249 Reference mangled_reference;
250 mangled_reference.id = attr.id;
251 mangled_reference.name = attr.name;
252 if (mangled_reference.name.value().package.empty()) {
253 mangled_reference.name.value().package =
254 context_->GetCompilationPackage();
255 }
256
257 if (Maybe<ResourceName> mangled_name =
258 context_->GetNameMangler()->MangleName(
259 mangled_reference.name.value())) {
260 mangled_reference.name = mangled_name;
261 }
262
263 // Look up the symbol so that we can write out in the comments what are
264 // possible
265 // legal values for this attribute.
266 const SymbolTable::Symbol* symbol =
267 context_->GetExternalSymbols()->FindByReference(mangled_reference);
268 if (symbol && symbol->attribute) {
269 // Copy the symbol data structure because the returned instance can be
270 // destroyed.
271 styleable_attr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
272 }
273 sorted_attributes.push_back(std::move(styleable_attr));
274 }
275
276 // Sort the attributes by ID.
277 std::sort(sorted_attributes.begin(), sorted_attributes.end(),
278 less_styleable_attr);
279
280 const size_t attr_count = sorted_attributes.size();
281 if (attr_count > 0) {
282 // Build the comment string for the Styleable. It includes details about the
283 // child attributes.
284 std::stringstream styleable_comment;
285 if (!styleable->GetComment().empty()) {
286 styleable_comment << styleable->GetComment() << "\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800287 } else {
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700288 styleable_comment << "Attributes that can be used with a " << class_name
289 << ".\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800290 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700291
292 styleable_comment << "<p>Includes the following attributes:</p>\n"
293 "<table>\n"
294 "<colgroup align=\"left\" />\n"
295 "<colgroup align=\"left\" />\n"
296 "<tr><th>Attribute</th><th>Description</th></tr>\n";
297
298 for (const StyleableAttr& entry : sorted_attributes) {
299 if (!entry.symbol) {
300 continue;
301 }
302
303 if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
304 !entry.symbol->is_public) {
305 // Don't write entries for non-public attributes.
306 continue;
307 }
308
309 StringPiece attr_comment_line = entry.symbol->attribute->GetComment();
310 if (attr_comment_line.contains("@removed")) {
311 // Removed attributes are public but hidden from the documentation, so
312 // don't emit
313 // them as part of the class documentation.
314 continue;
315 }
316
317 const ResourceName& attr_name = entry.attr_ref->name.value();
318 styleable_comment << "<tr><td>";
319 styleable_comment << "<code>{@link #" << entry.field_name << " "
320 << (!attr_name.package.empty()
321 ? attr_name.package
322 : context_->GetCompilationPackage())
323 << ":" << attr_name.entry << "}</code>";
324 styleable_comment << "</td>";
325
326 styleable_comment << "<td>";
327
328 // Only use the comment up until the first '.'. This is to stay compatible
329 // with
330 // the way old AAPT did it (presumably to keep it short and to avoid
331 // including
332 // annotations like @hide which would affect this Styleable).
333 auto iter =
334 std::find(attr_comment_line.begin(), attr_comment_line.end(), u'.');
335 if (iter != attr_comment_line.end()) {
336 attr_comment_line =
337 attr_comment_line.substr(0, (iter - attr_comment_line.begin()) + 1);
338 }
339 styleable_comment << attr_comment_line << "</td></tr>\n";
340 }
341 styleable_comment << "</table>\n";
342
343 for (const StyleableAttr& entry : sorted_attributes) {
344 if (!entry.symbol) {
345 continue;
346 }
347
348 if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
349 !entry.symbol->is_public) {
350 // Don't write entries for non-public attributes.
351 continue;
352 }
353 styleable_comment << "@see #" << entry.field_name << "\n";
354 }
355
356 styleable_array_def->GetCommentBuilder()->AppendComment(
357 styleable_comment.str());
358 }
359
360 // Add the ResourceIds to the array member.
361 for (const StyleableAttr& styleable_attr : sorted_attributes) {
362 styleable_array_def->AddElement(styleable_attr.attr_ref->id
363 ? styleable_attr.attr_ref->id.value()
364 : ResourceId(0));
365 }
366
367 // Add the Styleable array to the Styleable class.
368 out_styleable_class_def->AddMember(std::move(styleable_array_def));
369
370 // Now we emit the indices into the array.
371 for (size_t i = 0; i < attr_count; i++) {
372 const StyleableAttr& styleable_attr = sorted_attributes[i];
373
374 if (!styleable_attr.symbol) {
375 continue;
376 }
377
378 if (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
379 !styleable_attr.symbol->is_public) {
380 // Don't write entries for non-public attributes.
381 continue;
382 }
383
384 StringPiece comment = styleable_attr.attr_ref->GetComment();
385 if (styleable_attr.symbol->attribute && comment.empty()) {
386 comment = styleable_attr.symbol->attribute->GetComment();
387 }
388
389 if (comment.contains("@removed")) {
390 // Removed attributes are public but hidden from the documentation, so
391 // don't emit them
392 // as part of the class documentation.
393 continue;
394 }
395
396 const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
397
398 StringPiece package_name = attr_name.package;
399 if (package_name.empty()) {
400 package_name = context_->GetCompilationPackage();
401 }
402
403 std::unique_ptr<IntMember> index_member = util::make_unique<IntMember>(
404 sorted_attributes[i].field_name, static_cast<uint32_t>(i));
405
406 AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
407
408 if (!comment.empty()) {
409 attr_processor->AppendComment("<p>\n@attr description");
410 attr_processor->AppendComment(comment);
411 } else {
412 std::stringstream default_comment;
413 default_comment << "<p>This symbol is the offset where the "
414 << "{@link " << package_name << ".R.attr#"
415 << Transform(attr_name.entry) << "}\n"
416 << "attribute's value can be found in the "
417 << "{@link #" << class_name << "} array.";
418 attr_processor->AppendComment(default_comment.str());
419 }
420
421 attr_processor->AppendNewLine();
422
423 AddAttributeFormatDoc(attr_processor,
424 styleable_attr.symbol->attribute.get());
425 attr_processor->AppendNewLine();
426
427 std::stringstream doclava_name;
428 doclava_name << "@attr name " << package_name << ":" << attr_name.entry;
429
430 attr_processor->AppendComment(doclava_name.str());
431
432 out_styleable_class_def->AddMember(std::move(index_member));
433 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800434}
435
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700436bool JavaClassGenerator::AddMembersToTypeClass(
437 const StringPiece& package_name_to_generate,
438 const ResourceTablePackage* package, const ResourceTableType* type,
439 ClassDefinition* out_type_class_def) {
440 for (const auto& entry : type->entries) {
441 if (SkipSymbol(entry->symbol_status.state)) {
442 continue;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800443 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700444
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700445 ResourceId id;
446 if (package->id && type->id && entry->id) {
447 id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800448 }
449
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700450 std::string unmangled_package;
451 std::string unmangled_name = entry->name;
452 if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
453 // The entry name was mangled, and we successfully unmangled it.
454 // Check that we want to emit this symbol.
455 if (package->name != unmangled_package) {
456 // Skip the entry if it doesn't belong to the package we're writing.
457 continue;
458 }
459 } else if (package_name_to_generate != package->name) {
460 // We are processing a mangled package name,
461 // but this is a non-mangled resource.
462 continue;
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700463 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800464
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700465 if (!IsValidSymbol(unmangled_name)) {
466 ResourceNameRef resource_name(package_name_to_generate, type->type,
467 unmangled_name);
468 std::stringstream err;
469 err << "invalid symbol name '" << resource_name << "'";
470 error_ = err.str();
471 return false;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700472 }
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700473
474 if (type->type == ResourceType::kStyleable) {
475 CHECK(!entry->values.empty());
476
477 const Styleable* styleable =
478 static_cast<const Styleable*>(entry->values.front()->value.get());
479
480 // Comments are handled within this method.
481 AddMembersToStyleableClass(package_name_to_generate, unmangled_name,
482 styleable, out_type_class_def);
483 } else {
484 std::unique_ptr<ResourceMember> resource_member =
485 util::make_unique<ResourceMember>(Transform(unmangled_name), id);
486
487 // Build the comments and annotations for this entry.
488 AnnotationProcessor* processor = resource_member->GetCommentBuilder();
489
490 // Add the comments from any <public> tags.
491 if (entry->symbol_status.state != SymbolState::kUndefined) {
492 processor->AppendComment(entry->symbol_status.comment);
493 }
494
495 // Add the comments from all configurations of this entry.
496 for (const auto& config_value : entry->values) {
497 processor->AppendComment(config_value->value->GetComment());
498 }
499
500 // If this is an Attribute, append the format Javadoc.
501 if (!entry->values.empty()) {
502 if (Attribute* attr =
503 ValueCast<Attribute>(entry->values.front()->value.get())) {
504 // We list out the available values for the given attribute.
505 AddAttributeFormatDoc(processor, attr);
506 }
507 }
508
509 out_type_class_def->AddMember(std::move(resource_member));
510 }
511 }
512 return true;
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700513}
514
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700515bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
516 std::ostream* out) {
517 return Generate(package_name_to_generate, package_name_to_generate, out);
Adam Lesinski769de982015-04-10 19:43:55 -0700518}
519
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700520static void AppendJavaDocAnnotations(
521 const std::vector<std::string>& annotations,
522 AnnotationProcessor* processor) {
523 for (const std::string& annotation : annotations) {
524 std::string proper_annotation = "@";
525 proper_annotation += annotation;
526 processor->AppendComment(proper_annotation);
527 }
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700528}
529
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700530bool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
531 const StringPiece& out_package_name,
532 std::ostream* out) {
533 ClassDefinition r_class("R", ClassQualifier::None, true);
Adam Lesinski3524a232016-04-01 19:19:24 -0700534
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700535 for (const auto& package : table_->packages) {
536 for (const auto& type : package->types) {
537 if (type->type == ResourceType::kAttrPrivate) {
538 continue;
539 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800540
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700541 const bool force_creation_if_empty =
542 (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800543
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700544 std::unique_ptr<ClassDefinition> class_def =
545 util::make_unique<ClassDefinition>(ToString(type->type),
546 ClassQualifier::Static,
547 force_creation_if_empty);
Adam Lesinskib274e352015-11-06 15:14:35 -0800548
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700549 bool result = AddMembersToTypeClass(
550 package_name_to_generate, package.get(), type.get(), class_def.get());
551 if (!result) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700552 return false;
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700553 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700554
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700555 if (type->type == ResourceType::kAttr) {
556 // Also include private attributes in this same class.
557 ResourceTableType* priv_type =
558 package->FindType(ResourceType::kAttrPrivate);
559 if (priv_type) {
560 result =
561 AddMembersToTypeClass(package_name_to_generate, package.get(),
562 priv_type, class_def.get());
563 if (!result) {
564 return false;
565 }
566 }
567 }
568
569 if (type->type == ResourceType::kStyleable &&
570 options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
571 // When generating a public R class, we don't want Styleable to be part
572 // of the API.
573 // It is only emitted for documentation purposes.
574 class_def->GetCommentBuilder()->AppendComment("@doconly");
575 }
576
577 AppendJavaDocAnnotations(options_.javadoc_annotations,
578 class_def->GetCommentBuilder());
579
580 r_class.AddMember(std::move(class_def));
581 }
582 }
583
584 AppendJavaDocAnnotations(options_.javadoc_annotations,
585 r_class.GetCommentBuilder());
586
587 if (!ClassDefinition::WriteJavaFile(&r_class, out_package_name,
588 options_.use_final, out)) {
589 return false;
590 }
591
592 out->flush();
593 return true;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800594}
595
Adam Lesinskice5e56e2016-10-21 17:56:45 -0700596} // namespace aapt