16f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski/*
26f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Copyright (C) 2015 The Android Open Source Project
36f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
46f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
56f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * you may not use this file except in compliance with the License.
66f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * You may obtain a copy of the License at
76f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
86f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
96f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski *
106f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * Unless required by applicable law or agreed to in writing, software
116f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
126f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * See the License for the specific language governing permissions and
146f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski * limitations under the License.
156f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski */
166f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
17ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "java/JavaClassGenerator.h"
18ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
19ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <algorithm>
20ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <ostream>
21ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <set>
22ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <sstream>
23ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include <tuple>
24ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
25418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski#include "android-base/errors.h"
26ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "android-base/logging.h"
27ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski#include "android-base/stringprintf.h"
28d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski#include "androidfw/StringPiece.h"
29ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
30769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski#include "NameMangler.h"
316f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "Resource.h"
326f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceTable.h"
336f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski#include "ResourceValues.h"
341e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski#include "SdkConstants.h"
353b4cd94034ff3e5567a2ba6da35d640ff61db4b9Adam Lesinski#include "ValueVisitor.h"
36ca5638fd85098c3d0a699492751043545f75553aAdam Lesinski#include "java/AnnotationProcessor.h"
376cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski#include "java/ClassDefinition.h"
387656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski#include "process/SymbolTable.h"
39d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski
40a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinskiusing ::aapt::io::OutputStream;
41a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinskiusing ::aapt::text::Printer;
42a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinskiusing ::android::StringPiece;
43a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinskiusing ::android::base::StringPrintf;
446f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
456f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinskinamespace aapt {
466f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
47d0f116b619feede0cfdb647157ce5ab4d50a1c46Adam Lesinskistatic const std::set<StringPiece> sJavaIdentifiers = {
48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "abstract",   "assert",       "boolean",   "break",      "byte",
49ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "case",       "catch",        "char",      "class",      "const",
50ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "continue",   "default",      "do",        "double",     "else",
51ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "enum",       "extends",      "final",     "finally",    "float",
52ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "for",        "goto",         "if",        "implements", "import",
53ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "instanceof", "int",          "interface", "long",       "native",
54ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "new",        "package",      "private",   "protected",  "public",
55ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "return",     "short",        "static",    "strictfp",   "super",
56ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "switch",     "synchronized", "this",      "throw",      "throws",
57ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "transient",  "try",          "void",      "volatile",   "while",
58ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    "true",       "false",        "null"};
59ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
60ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic bool IsValidSymbol(const StringPiece& symbol) {
61ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
626f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
636f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
64ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// Java symbols can not contain . or -, but those are valid in a resource name.
65ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// Replace those with '_'.
66dc21dea9b8b1157a4a9347b68301da4307c51168Adam Koskistd::string JavaClassGenerator::TransformToFieldName(const StringPiece& symbol) {
67d5083f6f6b9bc76bbe64052bcec639eee752a321Adam Lesinski  std::string output = symbol.to_string();
68ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (char& c : output) {
69ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (c == '.' || c == '-') {
70ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      c = '_';
716f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
72ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
73ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return output;
746f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
756f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
76ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// Transforms an attribute in a styleable to the Java field name:
77ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski//
78ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// <declare-styleable name="Foo">
79ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski//   <attr name="android:bar" />
80ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski//   <attr name="bar" />
81ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// </declare-styleable>
82ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski//
83ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// Foo_android_bar
84ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// Foo_bar
85ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskistatic std::string TransformNestedAttr(const ResourceNameRef& attr_name,
86ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                       const std::string& styleable_class_name,
87ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                       const StringPiece& package_name_to_generate) {
88ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string output = styleable_class_name;
89ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
90ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // We may reference IDs from other packages, so prefix the entry name with
91ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // the package.
92ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (!attr_name.package.empty() &&
93ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      package_name_to_generate != attr_name.package) {
94dc21dea9b8b1157a4a9347b68301da4307c51168Adam Koski    output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.package);
95ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
96dc21dea9b8b1157a4a9347b68301da4307c51168Adam Koski  output += "_" + JavaClassGenerator::TransformToFieldName(attr_name.entry);
97ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return output;
9874605cd40256ca75b44cc3182eeeb886c92d737cAdam Lesinski}
9974605cd40256ca75b44cc3182eeeb886c92d737cAdam Lesinski
100ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskistatic void AddAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
101ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const uint32_t type_mask = attr->type_mask;
102ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_REFERENCE) {
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a reference to another resource, in the form\n"
105ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a "
106ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "theme\n"
107ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "attribute in the form\n"
108ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
109ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
110ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
111ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_STRING) {
112ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
113ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a string value, using '\\\\;' to escape characters such as\n"
114ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "'\\\\n' or '\\\\uxxxx' for a unicode character;");
115ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
116ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
117ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_INTEGER) {
118ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
119ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be an integer value, such as \"<code>100</code>\".");
120ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
121ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
122ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_BOOLEAN) {
123ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
125ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "\"<code>false</code>\".");
126ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
127ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
128ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_COLOR) {
129ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a color value, in the form of "
131ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "\"<code>#<i>rgb</i></code>\",\n"
132ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code>\", or \n"
133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "\"<code>#<i>aarrggbb</i></code>\".");
134ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
135ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
136ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_FLOAT) {
137ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
138ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a floating point value, such as \"<code>1.2</code>\".");
139ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
140ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
141ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_DIMENSION) {
142ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
143ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a dimension value, which is a floating point number "
144ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "appended with a\n"
145ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "unit such as \"<code>14.5sp</code>\".\n"
146ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "Available units are: px (pixels), dp (density-independent pixels),\n"
147ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "sp (scaled pixels based on preferred font size), in (inches), and\n"
148ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "mm (millimeters).");
149ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
150ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
151ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask & android::ResTable_map::TYPE_FRACTION) {
152ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
153ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<p>May be a fractional value, which is a floating point number "
154ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "appended with\n"
155ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "either % or %p, such as \"<code>14.5%</code>\".\n"
156ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "The % suffix always means a percentage of the base size;\n"
157ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "the optional %p suffix provides a size relative to some parent "
158ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "container.");
159ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
160ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
161ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (type_mask &
162ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
163ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (type_mask & android::ResTable_map::TYPE_FLAGS) {
164ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      processor->AppendComment(
165ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          "<p>Must be one or more (separated by '|') of the following "
166ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          "constant values.</p>");
167ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else {
168ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      processor->AppendComment(
169ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          "<p>Must be one of the following constant values.</p>");
1707656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski    }
1717656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
172ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(
173ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<table>\n<colgroup align=\"left\" />\n"
174ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<colgroup align=\"left\" />\n"
175ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<colgroup align=\"left\" />\n"
176ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
177ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const Attribute::Symbol& symbol : attr->symbols) {
178ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream line;
179ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
180ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski           << "<td>" << std::hex << symbol.value << std::dec << "</td>"
181ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski           << "<td>" << util::TrimWhitespace(symbol.symbol.GetComment())
182ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski           << "</td></tr>";
183ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      processor->AppendComment(line.str());
1847656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski    }
185ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment("</table>");
186ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
187ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski}
1887656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
189ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiJavaClassGenerator::JavaClassGenerator(IAaptContext* context,
190ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                       ResourceTable* table,
191ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                       const JavaClassGeneratorOptions& options)
192ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    : context_(context), table_(table), options_(options) {}
1937656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
19471be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinskibool JavaClassGenerator::SkipSymbol(Visibility::Level level) {
195ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  switch (options_.types) {
196ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    case JavaClassGeneratorOptions::SymbolTypes::kAll:
197ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      return false;
198ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
19971be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      return level == Visibility::Level::kUndefined;
200ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    case JavaClassGeneratorOptions::SymbolTypes::kPublic:
20171be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      return level != Visibility::Level::kPublic;
202ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
203ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return true;
204ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski}
2057656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
206ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski// Whether or not to skip writing this symbol.
207ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskibool JavaClassGenerator::SkipSymbol(const Maybe<SymbolTable::Symbol>& symbol) {
208ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  return !symbol || (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
209ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                     !symbol.value().is_public);
210ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski}
211ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
212ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistruct StyleableAttr {
213ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  const Reference* attr_ref = nullptr;
214ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::string field_name;
215ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  Maybe<SymbolTable::Symbol> symbol;
216ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski};
2177656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
218ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskistatic bool operator<(const StyleableAttr& lhs, const StyleableAttr& rhs) {
219ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  const ResourceId lhs_id = lhs.attr_ref->id.value_or_default(ResourceId(0));
220ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  const ResourceId rhs_id = rhs.attr_ref->id.value_or_default(ResourceId(0));
221ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  if (lhs_id < rhs_id) {
222ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return true;
223ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  } else if (lhs_id > rhs_id) {
224ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return false;
225ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  } else {
226ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    return lhs.attr_ref->name.value() < rhs.attr_ref->name.value();
227ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
228ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski}
2297656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
230ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskivoid JavaClassGenerator::ProcessStyleable(const ResourceNameRef& name, const ResourceId& id,
231ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                          const Styleable& styleable,
232ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                          const StringPiece& package_name_to_generate,
233ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                          ClassDefinition* out_class_def,
234418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski                                          MethodDefinition* out_rewrite_method,
235a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                                          Printer* r_txt_printer) {
236ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  const std::string array_field_name = TransformToFieldName(name.entry);
237ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  std::unique_ptr<ResourceArrayMember> array_def =
238ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      util::make_unique<ResourceArrayMember>(array_field_name);
239ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
240ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  // The array must be sorted by resource ID.
241ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  std::vector<StyleableAttr> sorted_attributes;
242ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  sorted_attributes.reserve(styleable.entries.size());
243ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  for (const auto& attr : styleable.entries) {
244ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // If we are not encoding final attributes, the styleable entry may have no
245ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // ID if we are building a static library.
246ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CHECK(!options_.use_final || attr.id) << "no ID set for Styleable entry";
247ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    CHECK(bool(attr.name)) << "no name set for Styleable entry";
248ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
249ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // We will need the unmangled, transformed name in the comments and the field,
250ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // so create it once and cache it in this StyleableAttr data structure.
251ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    StyleableAttr styleable_attr;
252ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    styleable_attr.attr_ref = &attr;
2537656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
254ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // The field name for this attribute is prefixed by the name of this styleable and
255ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // the package it comes from.
256ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    styleable_attr.field_name =
257ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        TransformNestedAttr(attr.name.value(), array_field_name, package_name_to_generate);
2587656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
259ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // Look up the symbol so that we can write out in the comments what are possible legal values
260ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // for this attribute.
261ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    const SymbolTable::Symbol* symbol = context_->GetExternalSymbols()->FindByReference(attr);
262ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (symbol && symbol->attribute) {
263ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      // Copy the symbol data structure because the returned instance can be destroyed.
264ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      styleable_attr.symbol = *symbol;
2657656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski    }
266ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    sorted_attributes.push_back(std::move(styleable_attr));
267ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
268ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
269ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // Sort the attributes by ID.
270ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  std::sort(sorted_attributes.begin(), sorted_attributes.end());
271ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
272ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  // Build the JavaDoc comment for the Styleable array. This has references to child attributes
273ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  // and what possible values can be used for them.
274ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const size_t attr_count = sorted_attributes.size();
27523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska  if (out_class_def != nullptr && attr_count > 0) {
276ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::stringstream styleable_comment;
277ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    if (!styleable.GetComment().empty()) {
278ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      styleable_comment << styleable.GetComment() << "\n";
279ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else {
280ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      // Apply a default intro comment if the styleable has no comments of its own.
281ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      styleable_comment << "Attributes that can be used with a " << array_field_name << ".\n";
2829e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski    }
28374605cd40256ca75b44cc3182eeeb886c92d737cAdam Lesinski
284ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    styleable_comment << "<p>Includes the following attributes:</p>\n"
285ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         "<table>\n"
286ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         "<colgroup align=\"left\" />\n"
287ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         "<colgroup align=\"left\" />\n"
288ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                         "<tr><th>Attribute</th><th>Description</th></tr>\n";
289ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
290ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // Build the table of attributes with their links and names.
291ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const StyleableAttr& entry : sorted_attributes) {
292ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      if (SkipSymbol(entry.symbol)) {
293ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        continue;
294ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
295ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
296ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      StringPiece attr_comment_line = entry.symbol.value().attribute->GetComment();
297ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (attr_comment_line.contains("@removed")) {
298ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        // Removed attributes are public but hidden from the documentation, so
299ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        // don't emit them as part of the class documentation.
300ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        continue;
301ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
302ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
303ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const ResourceName& attr_name = entry.attr_ref->name.value();
304e967d3f6ac2e1e1f612f99b9c76abcb9e13bb7a2Adam Lesinski      styleable_comment << "<tr><td><code>{@link #" << entry.field_name << " "
305e967d3f6ac2e1e1f612f99b9c76abcb9e13bb7a2Adam Lesinski                        << (!attr_name.package.empty() ? attr_name.package
306e967d3f6ac2e1e1f612f99b9c76abcb9e13bb7a2Adam Lesinski                                                       : context_->GetCompilationPackage())
307e967d3f6ac2e1e1f612f99b9c76abcb9e13bb7a2Adam Lesinski                        << ":" << attr_name.entry << "}</code></td>";
308ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
309ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      // Only use the comment up until the first '.'. This is to stay compatible with
310ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      // the way old AAPT did it (presumably to keep it short and to avoid including
311ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      // annotations like @hide which would affect this Styleable).
312e967d3f6ac2e1e1f612f99b9c76abcb9e13bb7a2Adam Lesinski      styleable_comment << "<td>" << AnnotationProcessor::ExtractFirstSentence(attr_comment_line)
313e967d3f6ac2e1e1f612f99b9c76abcb9e13bb7a2Adam Lesinski                        << "</td></tr>\n";
314ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    }
315ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    styleable_comment << "</table>\n";
316ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
317ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // Generate the @see lines for each attribute.
318ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const StyleableAttr& entry : sorted_attributes) {
319ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      if (SkipSymbol(entry.symbol)) {
320ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        continue;
321ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
322ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      styleable_comment << "@see #" << entry.field_name << "\n";
32374605cd40256ca75b44cc3182eeeb886c92d737cAdam Lesinski    }
32474605cd40256ca75b44cc3182eeeb886c92d737cAdam Lesinski
325ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    array_def->GetCommentBuilder()->AppendComment(styleable_comment.str());
326ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
3277656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
328a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  if (r_txt_printer != nullptr) {
329a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    r_txt_printer->Print("int[] styleable ").Print(array_field_name).Print(" {");
330418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski  }
331418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski
332ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // Add the ResourceIds to the array member.
333418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski  for (size_t i = 0; i < attr_count; i++) {
334418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski    const ResourceId id = sorted_attributes[i].attr_ref->id.value_or_default(ResourceId(0));
335ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    array_def->AddElement(id);
336418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski
337a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    if (r_txt_printer != nullptr) {
338418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski      if (i != 0) {
339a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski        r_txt_printer->Print(",");
340418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski      }
341a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski      r_txt_printer->Print(" ").Print(id.to_string());
342418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski    }
343418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski  }
344418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski
345a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  if (r_txt_printer != nullptr) {
346a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    r_txt_printer->Println(" }");
347ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
3487656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
349ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // Add the Styleable array to the Styleable class.
350ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  out_class_def->AddMember(std::move(array_def));
3516cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski
352ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  // Now we emit the indices into the array.
353ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (size_t i = 0; i < attr_count; i++) {
354ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    const StyleableAttr& styleable_attr = sorted_attributes[i];
355ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    if (SkipSymbol(styleable_attr.symbol)) {
356ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      continue;
3576f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski    }
3586f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
35923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    if (out_class_def != nullptr) {
36023a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      StringPiece comment = styleable_attr.attr_ref->GetComment();
36123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (styleable_attr.symbol.value().attribute && comment.empty()) {
36223a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        comment = styleable_attr.symbol.value().attribute->GetComment();
36323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
3646f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
36523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (comment.contains("@removed")) {
36623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        // Removed attributes are public but hidden from the documentation, so
36723a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        // don't emit them as part of the class documentation.
36823a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        continue;
36923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
3706f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
37123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      const ResourceName& attr_name = styleable_attr.attr_ref->name.value();
372626b3dbf74f02ae630ae0089632f5962340694dcAdam Lesinski
37323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      StringPiece package_name = attr_name.package;
37423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (package_name.empty()) {
37523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        package_name = context_->GetCompilationPackage();
37623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
377626b3dbf74f02ae630ae0089632f5962340694dcAdam Lesinski
37823a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      std::unique_ptr<IntMember> index_member =
37923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska          util::make_unique<IntMember>(sorted_attributes[i].field_name, static_cast<uint32_t>(i));
38023a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska
38123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      AnnotationProcessor* attr_processor = index_member->GetCommentBuilder();
38223a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska
38323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (!comment.empty()) {
38423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        attr_processor->AppendComment("<p>\n@attr description");
38523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        attr_processor->AppendComment(comment);
38623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      } else {
38723a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        std::stringstream default_comment;
38823a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        default_comment << "<p>This symbol is the offset where the "
38923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska                        << "{@link " << package_name << ".R.attr#"
39023a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska                        << TransformToFieldName(attr_name.entry) << "}\n"
39123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska                        << "attribute's value can be found in the "
39223a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska                        << "{@link #" << array_field_name << "} array.";
39323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        attr_processor->AppendComment(default_comment.str());
39423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
395626b3dbf74f02ae630ae0089632f5962340694dcAdam Lesinski
39623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      attr_processor->AppendNewLine();
39723a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      AddAttributeFormatDoc(attr_processor, styleable_attr.symbol.value().attribute.get());
39823a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      attr_processor->AppendNewLine();
39923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      attr_processor->AppendComment(
40023a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska          StringPrintf("@attr name %s:%s", package_name.data(), attr_name.entry.data()));
401feaf99fa1b7563f15dbd4211718a6cfb7a3cc3c8Michael Wright
40223a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      out_class_def->AddMember(std::move(index_member));
403ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    }
404feaf99fa1b7563f15dbd4211718a6cfb7a3cc3c8Michael Wright
405a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    if (r_txt_printer != nullptr) {
406a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski      r_txt_printer->Println(
407a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski          StringPrintf("int styleable %s %zd", sorted_attributes[i].field_name.c_str(), i));
408418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski    }
409ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  }
410ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
411ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  // If there is a rewrite method to generate, add the statements that rewrite package IDs
412ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  // for this styleable.
413ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  if (out_rewrite_method != nullptr) {
414ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    out_rewrite_method->AppendStatement(
415ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        StringPrintf("for (int i = 0; i < styleable.%s.length; i++) {", array_field_name.data()));
416ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    out_rewrite_method->AppendStatement(
417ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        StringPrintf("  if ((styleable.%s[i] & 0xff000000) == 0) {", array_field_name.data()));
418ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    out_rewrite_method->AppendStatement(
419ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        StringPrintf("    styleable.%s[i] = (styleable.%s[i] & 0x00ffffff) | (p << 24);",
420ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                     array_field_name.data(), array_field_name.data()));
421ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    out_rewrite_method->AppendStatement("  }");
422ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    out_rewrite_method->AppendStatement("}");
423ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  }
424ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski}
425ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
426ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskivoid JavaClassGenerator::ProcessResource(const ResourceNameRef& name, const ResourceId& id,
427ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                         const ResourceEntry& entry, ClassDefinition* out_class_def,
428418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski                                         MethodDefinition* out_rewrite_method,
429a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                                         text::Printer* r_txt_printer) {
4301e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski  ResourceId real_id = id;
4311e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski  if (context_->GetMinSdkVersion() < SDK_O && name.type == ResourceType::kId &&
4321e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski      id.package_id() > kAppPackageId) {
433a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    // Workaround for feature splits using package IDs > 0x7F.
434a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    // See b/37498913.
4351e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski    real_id = ResourceId(kAppPackageId, id.package_id(), id.entry_id());
4361e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski  }
4371e4b0e54a3db31bdbcb9385bf22bab4b96096d1fAdam Lesinski
438ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  const std::string field_name = TransformToFieldName(name.entry);
43923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska  if (out_class_def != nullptr) {
44023a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    std::unique_ptr<ResourceMember> resource_member =
44123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        util::make_unique<ResourceMember>(field_name, real_id);
442ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
44323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    // Build the comments and annotations for this entry.
44423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    AnnotationProcessor* processor = resource_member->GetCommentBuilder();
445ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
44623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    // Add the comments from any <public> tags.
44771be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski    if (entry.visibility.level != Visibility::Level::kUndefined) {
44871be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski      processor->AppendComment(entry.visibility.comment);
44923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    }
4503b4cd94034ff3e5567a2ba6da35d640ff61db4b9Adam Lesinski
45123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    // Add the comments from all configurations of this entry.
45223a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    for (const auto& config_value : entry.values) {
45323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      processor->AppendComment(config_value->value->GetComment());
45423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    }
455ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
45623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    // If this is an Attribute, append the format Javadoc.
45723a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    if (!entry.values.empty()) {
45823a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (Attribute* attr = ValueCast<Attribute>(entry.values.front()->value.get())) {
45923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        // We list out the available values for the given attribute.
46023a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        AddAttributeFormatDoc(processor, attr);
46123a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
462ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    }
4633b4cd94034ff3e5567a2ba6da35d640ff61db4b9Adam Lesinski
46423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    out_class_def->AddMember(std::move(resource_member));
46523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska  }
4667656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
467a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  if (r_txt_printer != nullptr) {
468a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    r_txt_printer->Print("int ")
469a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski        .Print(to_string(name.type))
470a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski        .Print(" ")
471a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski        .Print(field_name)
472a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski        .Print(" ")
473a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski        .Println(real_id.to_string());
474418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski  }
475418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski
476ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  if (out_rewrite_method != nullptr) {
47793190b79d11d874199cfe7258526a48cfc8399fcAdam Lesinski    const StringPiece& type_str = to_string(name.type);
478ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    out_rewrite_method->AppendStatement(StringPrintf("%s.%s = (%s.%s & 0x00ffffff) | (p << 24);",
479ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                                     type_str.data(), field_name.data(),
480ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                                     type_str.data(), field_name.data()));
481ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
482ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski}
4837656554f91b40bc93bf94c89afcad4a9a8ced884Adam Lesinski
484ceb9b2f80f853059233cdd29057f39a5960a74aeAdam LesinskiMaybe<std::string> JavaClassGenerator::UnmangleResource(const StringPiece& package_name,
485ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                                        const StringPiece& package_name_to_generate,
486ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                                        const ResourceEntry& entry) {
48771be70507de9cb619b644e55eda1cc181e3f7e90Adam Lesinski  if (SkipSymbol(entry.visibility.level)) {
488ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    return {};
489ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  }
490ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
491ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  std::string unmangled_package;
492ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  std::string unmangled_name = entry.name;
493ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  if (NameMangler::Unmangle(&unmangled_name, &unmangled_package)) {
494ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // The entry name was mangled, and we successfully unmangled it.
495ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // Check that we want to emit this symbol.
4961ef0fa9d7242b1926543bc49e35905d1be02a781Adam Lesinski    if (package_name_to_generate != unmangled_package) {
497ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      // Skip the entry if it doesn't belong to the package we're writing.
498ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      return {};
499ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    }
500ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  } else if (package_name_to_generate != package_name) {
501ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // We are processing a mangled package name,
502ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // but this is a non-mangled resource.
503ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    return {};
504ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  }
505ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  return {std::move(unmangled_name)};
506ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski}
507ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
508ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskibool JavaClassGenerator::ProcessType(const StringPiece& package_name_to_generate,
509ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                     const ResourceTablePackage& package,
510ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                     const ResourceTableType& type,
511ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                     ClassDefinition* out_type_class_def,
512418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski                                     MethodDefinition* out_rewrite_method_def,
513a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                                     Printer* r_txt_printer) {
514ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  for (const auto& entry : type.entries) {
515ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    const Maybe<std::string> unmangled_name =
516ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        UnmangleResource(package.name, package_name_to_generate, *entry);
517ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    if (!unmangled_name) {
518ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      continue;
519ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    }
5206cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski
521ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // Create an ID if there is one (static libraries don't need one).
522ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ResourceId id;
523ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    if (package.id && type.id && entry->id) {
524ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      id = ResourceId(package.id.value(), type.id.value(), entry->id.value());
5253b4cd94034ff3e5567a2ba6da35d640ff61db4b9Adam Lesinski    }
5263b4cd94034ff3e5567a2ba6da35d640ff61db4b9Adam Lesinski
527ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // We need to make sure we hide the fact that we are generating kAttrPrivate attributes.
528ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    const ResourceNameRef resource_name(
529ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        package_name_to_generate,
530ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        type.type == ResourceType::kAttrPrivate ? ResourceType::kAttr : type.type,
531ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        unmangled_name.value());
5326cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski
533ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    // Check to see if the unmangled name is a valid Java name (not a keyword).
534ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    if (!IsValidSymbol(unmangled_name.value())) {
535ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::stringstream err;
536ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      err << "invalid symbol name '" << resource_name << "'";
537ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      error_ = err.str();
538ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      return false;
539ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    }
5409e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
541ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    if (resource_name.type == ResourceType::kStyleable) {
542ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      CHECK(!entry->values.empty());
543769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
544ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const Styleable* styleable =
545ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          static_cast<const Styleable*>(entry->values.front()->value.get());
546769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
547ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      ProcessStyleable(resource_name, id, *styleable, package_name_to_generate, out_type_class_def,
548a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                       out_rewrite_method_def, r_txt_printer);
549ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    } else {
550418763ff54170484c527bf618ef2fad34fe63f97Adam Lesinski      ProcessResource(resource_name, id, *entry, out_type_class_def, out_rewrite_method_def,
551a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                      r_txt_printer);
552769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski    }
553ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
554ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return true;
555769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski}
556769de98f2dd41bfe39a1c9f76aefd1ad58942733Adam Lesinski
557a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinskibool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate, OutputStream* out,
558a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                                  OutputStream* out_r_txt) {
559a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  return Generate(package_name_to_generate, package_name_to_generate, out, out_r_txt);
5609e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski}
5619e10ac70155c993e7053323ad36beaea7bf7d54fAdam Lesinski
562ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinskistatic void AppendJavaDocAnnotations(const std::vector<std::string>& annotations,
563ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski                                     AnnotationProcessor* processor) {
564ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (const std::string& annotation : annotations) {
565ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    std::string proper_annotation = "@";
566ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    proper_annotation += annotation;
567ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    processor->AppendComment(proper_annotation);
568ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
5693524a23edb88f0e67352d55ac6a2919f1edf7b30Adam Lesinski}
5703524a23edb88f0e67352d55ac6a2919f1edf7b30Adam Lesinski
571ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool JavaClassGenerator::Generate(const StringPiece& package_name_to_generate,
572a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                                  const StringPiece& out_package_name, OutputStream* out,
573a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                                  OutputStream* out_r_txt) {
574ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  ClassDefinition r_class("R", ClassQualifier::kNone, true);
575ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  std::unique_ptr<MethodDefinition> rewrite_method;
576ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
577a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  std::unique_ptr<Printer> r_txt_printer;
578a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  if (out_r_txt != nullptr) {
579a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski    r_txt_printer = util::make_unique<Printer>(out_r_txt);
580a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski  }
581a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski
582ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  // Generate an onResourcesLoaded() callback if requested.
58323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska  if (out != nullptr && options_.rewrite_callback_options) {
584ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    rewrite_method =
585ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        util::make_unique<MethodDefinition>("public static void onResourcesLoaded(int p)");
586b5dc4bd49a036e3403ca17e961d2c8e13e038295Adam Lesinski    for (const std::string& package_to_callback :
587b5dc4bd49a036e3403ca17e961d2c8e13e038295Adam Lesinski         options_.rewrite_callback_options.value().packages_to_callback) {
588b5dc4bd49a036e3403ca17e961d2c8e13e038295Adam Lesinski      rewrite_method->AppendStatement(
589b5dc4bd49a036e3403ca17e961d2c8e13e038295Adam Lesinski          StringPrintf("%s.R.onResourcesLoaded(p);", package_to_callback.data()));
590b5dc4bd49a036e3403ca17e961d2c8e13e038295Adam Lesinski    }
591ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  }
592ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
593ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (const auto& package : table_->packages) {
594ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (const auto& type : package->types) {
595ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (type->type == ResourceType::kAttrPrivate) {
596ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        // We generate these as part of the kAttr type, so skip them here.
597ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        continue;
598ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
5996f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
6001ef0fa9d7242b1926543bc49e35905d1be02a781Adam Lesinski      // Stay consistent with AAPT and generate an empty type class if the R class is public.
601ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      const bool force_creation_if_empty =
602ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          (options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
6033524a23edb88f0e67352d55ac6a2919f1edf7b30Adam Lesinski
60423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      std::unique_ptr<ClassDefinition> class_def;
60523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (out != nullptr) {
60623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        class_def = util::make_unique<ClassDefinition>(
60723a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska            to_string(type->type), ClassQualifier::kStatic, force_creation_if_empty);
60823a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
60923a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska
610ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski      if (!ProcessType(package_name_to_generate, *package, *type, class_def.get(),
611a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                       rewrite_method.get(), r_txt_printer.get())) {
6126cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski        return false;
613ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
614ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
615ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (type->type == ResourceType::kAttr) {
616ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        // Also include private attributes in this same class.
617ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        const ResourceTableType* priv_type = package->FindType(ResourceType::kAttrPrivate);
618ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        if (priv_type) {
619ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski          if (!ProcessType(package_name_to_generate, *package, *priv_type, class_def.get(),
620a693c4a32ebed4e96dcc1cf6a706e8ebbb004db2Adam Lesinski                           rewrite_method.get(), r_txt_printer.get())) {
621ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski            return false;
622ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          }
623ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        }
624ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
625ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
62623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (out != nullptr && type->type == ResourceType::kStyleable &&
627ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          options_.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
628ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        // When generating a public R class, we don't want Styleable to be part
629ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski        // of the API. It is only emitted for documentation purposes.
630ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        class_def->GetCommentBuilder()->AppendComment("@doconly");
631ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      }
632ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
63323a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      if (out != nullptr) {
63423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        AppendJavaDocAnnotations(options_.javadoc_annotations, class_def->GetCommentBuilder());
63523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska        r_class.AddMember(std::move(class_def));
63623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska      }
6376cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski    }
638ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  }
6396cbfb1de493e42d937158ed57495c9656864ccbaAdam Lesinski
640ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  if (rewrite_method != nullptr) {
641ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski    r_class.AddMember(std::move(rewrite_method));
642ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski  }
643ceb9b2f80f853059233cdd29057f39a5960a74aeAdam Lesinski
64423a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska  if (out != nullptr) {
64523a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    AppendJavaDocAnnotations(options_.javadoc_annotations, r_class.GetCommentBuilder());
64623a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska    ClassDefinition::WriteJavaFile(&r_class, out_package_name, options_.use_final, out);
64723a6e1e3901e1ef1e2bd5ebb2aff08b767d19c49Izabela Orlowska  }
648ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  return true;
6496f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski}
6506f6ceb7e1456698b1f33e04536bfb3227f9fcfcbAdam Lesinski
651ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski}  // namespace aapt
652