blob: fbaefb1f0c80bac2619d579b6585510bad98e8a7 [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 Lesinski769de982015-04-10 19:43:55 -070017#include "NameMangler.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018#include "Resource.h"
19#include "ResourceTable.h"
20#include "ResourceValues.h"
Adam Lesinski3b4cd942015-10-30 16:31:42 -070021#include "ValueVisitor.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070022#include "java/AnnotationProcessor.h"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -070023#include "java/ClassDefinition.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070024#include "java/JavaClassGenerator.h"
Adam Lesinski76565542016-03-10 21:55:04 -080025#include "process/SymbolTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070026#include "util/StringPiece.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080027
Adam Lesinskica2fc352015-04-03 12:08:26 -070028#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080029#include <ostream>
30#include <set>
31#include <sstream>
32#include <tuple>
33
34namespace aapt {
35
Adam Lesinski76565542016-03-10 21:55:04 -080036JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* table,
37 const JavaClassGeneratorOptions& options) :
38 mContext(context), mTable(table), mOptions(options) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080039}
40
Adam Lesinskid0f116b2016-07-08 15:00:32 -070041static const std::set<StringPiece> sJavaIdentifiers = {
42 "abstract", "assert", "boolean", "break", "byte",
43 "case", "catch", "char", "class", "const", "continue",
44 "default", "do", "double", "else", "enum", "extends",
45 "final", "finally", "float", "for", "goto", "if",
46 "implements", "import", "instanceof", "int", "interface",
47 "long", "native", "new", "package", "private", "protected",
48 "public", "return", "short", "static", "strictfp", "super",
49 "switch", "synchronized", "this", "throw", "throws",
50 "transient", "try", "void", "volatile", "while", "true",
51 "false", "null"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080052};
53
Adam Lesinskid0f116b2016-07-08 15:00:32 -070054static bool isValidSymbol(const StringPiece& symbol) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080055 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
56}
57
58/*
59 * Java symbols can not contain . or -, but those are valid in a resource name.
60 * Replace those with '_'.
61 */
Adam Lesinskid0f116b2016-07-08 15:00:32 -070062static std::string transform(const StringPiece& symbol) {
63 std::string output = symbol.toString();
Adam Lesinski74605cd2016-03-03 15:39:50 -080064 for (char& c : output) {
65 if (c == '.' || c == '-') {
66 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080067 }
68 }
69 return output;
70}
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 */
83static std::string transformNestedAttr(const ResourceNameRef& attrName,
84 const std::string& styleableClassName,
Adam Lesinskid0f116b2016-07-08 15:00:32 -070085 const StringPiece& packageNameToGenerate) {
Adam Lesinski74605cd2016-03-03 15:39:50 -080086 std::string output = styleableClassName;
87
88 // We may reference IDs from other packages, so prefix the entry name with
89 // the package.
90 if (!attrName.package.empty() && packageNameToGenerate != attrName.package) {
91 output += "_" + transform(attrName.package);
92 }
93 output += "_" + transform(attrName.entry);
94 return output;
95}
96
Adam Lesinski76565542016-03-10 21:55:04 -080097static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
98 const uint32_t typeMask = attr->typeMask;
99 if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
100 processor->appendComment(
101 "<p>May be a reference to another resource, in the form\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700102 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
103 "attribute in the form\n"
104 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
Adam Lesinski76565542016-03-10 21:55:04 -0800105 }
106
107 if (typeMask & android::ResTable_map::TYPE_STRING) {
108 processor->appendComment(
109 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700110 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
Adam Lesinski76565542016-03-10 21:55:04 -0800111 }
112
113 if (typeMask & android::ResTable_map::TYPE_INTEGER) {
114 processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
115 }
116
117 if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
118 processor->appendComment(
119 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700120 "\"<code>false</code>\".");
Adam Lesinski76565542016-03-10 21:55:04 -0800121 }
122
123 if (typeMask & android::ResTable_map::TYPE_COLOR) {
124 processor->appendComment(
125 "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700126 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
127 "\"<code>#<i>aarrggbb</i></code>\".");
Adam Lesinski76565542016-03-10 21:55:04 -0800128 }
129
130 if (typeMask & android::ResTable_map::TYPE_FLOAT) {
131 processor->appendComment(
132 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
133 }
134
135 if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
136 processor->appendComment(
137 "<p>May be a dimension value, which is a floating point number appended with a\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700138 "unit such as \"<code>14.5sp</code>\".\n"
139 "Available units are: px (pixels), dp (density-independent pixels),\n"
140 "sp (scaled pixels based on preferred font size), in (inches), and\n"
141 "mm (millimeters).");
Adam Lesinski76565542016-03-10 21:55:04 -0800142 }
143
144 if (typeMask & android::ResTable_map::TYPE_FRACTION) {
145 processor->appendComment(
146 "<p>May be a fractional value, which is a floating point number appended with\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700147 "either % or %p, such as \"<code>14.5%</code>\".\n"
148 "The % suffix always means a percentage of the base size;\n"
149 "the optional %p suffix provides a size relative to some parent container.");
Adam Lesinski76565542016-03-10 21:55:04 -0800150 }
151
152 if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
153 if (typeMask & android::ResTable_map::TYPE_FLAGS) {
154 processor->appendComment(
155 "<p>Must be one or more (separated by '|') of the following "
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700156 "constant values.</p>");
Adam Lesinski76565542016-03-10 21:55:04 -0800157 } else {
158 processor->appendComment("<p>Must be one of the following constant values.</p>");
159 }
160
161 processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700162 "<colgroup align=\"left\" />\n"
163 "<colgroup align=\"left\" />\n"
164 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
Adam Lesinski76565542016-03-10 21:55:04 -0800165 for (const Attribute::Symbol& symbol : attr->symbols) {
166 std::stringstream line;
167 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
168 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
169 << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
170 processor->appendComment(line.str());
171 }
172 processor->appendComment("</table>");
173 }
174}
175
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700176bool JavaClassGenerator::skipSymbol(SymbolState state) {
177 switch (mOptions.types) {
178 case JavaClassGeneratorOptions::SymbolTypes::kAll:
179 return false;
180 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
181 return state == SymbolState::kUndefined;
182 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
183 return state != SymbolState::kPublic;
184 }
185 return true;
186}
187
Adam Lesinski74605cd2016-03-03 15:39:50 -0800188struct StyleableAttr {
189 const Reference* attrRef;
190 std::string fieldName;
Adam Lesinski626b3db2016-04-07 13:24:59 -0700191 std::unique_ptr<SymbolTable::Symbol> symbol;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800192};
193
194static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs) {
195 const ResourceId lhsId = lhs.attrRef->id ? lhs.attrRef->id.value() : ResourceId(0);
196 const ResourceId rhsId = rhs.attrRef->id ? rhs.attrRef->id.value() : ResourceId(0);
197 if (lhsId < rhsId) {
198 return true;
199 } else if (lhsId > rhsId) {
200 return false;
201 } else {
202 return lhs.attrRef->name.value() < rhs.attrRef->name.value();
203 }
204}
205
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700206void JavaClassGenerator::addMembersToStyleableClass(const StringPiece& packageNameToGenerate,
207 const std::string& entryName,
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700208 const Styleable* styleable,
209 ClassDefinition* outStyleableClassDef) {
Adam Lesinski74605cd2016-03-03 15:39:50 -0800210 const std::string className = transform(entryName);
211
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700212 std::unique_ptr<ResourceArrayMember> styleableArrayDef =
213 util::make_unique<ResourceArrayMember>(className);
214
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800215 // This must be sorted by resource ID.
Adam Lesinski74605cd2016-03-03 15:39:50 -0800216 std::vector<StyleableAttr> sortedAttributes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700217 sortedAttributes.reserve(styleable->entries.size());
218 for (const auto& attr : styleable->entries) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700219 // If we are not encoding final attributes, the styleable entry may have no ID
220 // if we are building a static library.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
222 assert(attr.name && "no name set for Styleable entry");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800223
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700224 // We will need the unmangled, transformed name in the comments and the field,
225 // so create it once and cache it in this StyleableAttr data structure.
Adam Lesinski76565542016-03-10 21:55:04 -0800226 StyleableAttr styleableAttr = {};
227 styleableAttr.attrRef = &attr;
228 styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
229 packageNameToGenerate);
230
231 Reference mangledReference;
232 mangledReference.id = attr.id;
233 mangledReference.name = attr.name;
234 if (mangledReference.name.value().package.empty()) {
235 mangledReference.name.value().package = mContext->getCompilationPackage();
236 }
237
238 if (Maybe<ResourceName> mangledName =
239 mContext->getNameMangler()->mangleName(mangledReference.name.value())) {
240 mangledReference.name = mangledName;
241 }
242
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700243 // Look up the symbol so that we can write out in the comments what are possible
244 // legal values for this attribute.
Adam Lesinski76565542016-03-10 21:55:04 -0800245 const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
246 mangledReference);
Adam Lesinski626b3db2016-04-07 13:24:59 -0700247 if (symbol && symbol->attribute) {
248 // Copy the symbol data structure because the returned instance can be destroyed.
249 styleableAttr.symbol = util::make_unique<SymbolTable::Symbol>(*symbol);
Adam Lesinski76565542016-03-10 21:55:04 -0800250 }
251 sortedAttributes.push_back(std::move(styleableAttr));
Adam Lesinski74605cd2016-03-03 15:39:50 -0800252 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700253
254 // Sort the attributes by ID.
Adam Lesinski74605cd2016-03-03 15:39:50 -0800255 std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
256
257 const size_t attrCount = sortedAttributes.size();
Adam Lesinski74605cd2016-03-03 15:39:50 -0800258 if (attrCount > 0) {
259 // Build the comment string for the Styleable. It includes details about the
260 // child attributes.
261 std::stringstream styleableComment;
Adam Lesinski76565542016-03-10 21:55:04 -0800262 if (!styleable->getComment().empty()) {
263 styleableComment << styleable->getComment() << "\n";
264 } else {
265 styleableComment << "Attributes that can be used with a " << className << ".\n";
266 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700267
Adam Lesinski76565542016-03-10 21:55:04 -0800268 styleableComment <<
269 "<p>Includes the following attributes:</p>\n"
270 "<table>\n"
Adam Lesinski74605cd2016-03-03 15:39:50 -0800271 "<colgroup align=\"left\" />\n"
Adam Lesinski76565542016-03-10 21:55:04 -0800272 "<colgroup align=\"left\" />\n"
Adam Lesinski74605cd2016-03-03 15:39:50 -0800273 "<tr><th>Attribute</th><th>Description</th></tr>\n";
Adam Lesinski76565542016-03-10 21:55:04 -0800274
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700275 for (const StyleableAttr& entry : sortedAttributes) {
Adam Lesinski626b3db2016-04-07 13:24:59 -0700276 if (!entry.symbol) {
277 continue;
278 }
279
280 if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
281 !entry.symbol->isPublic) {
282 // Don't write entries for non-public attributes.
283 continue;
284 }
285
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700286 StringPiece attrCommentLine = entry.symbol->attribute->getComment();
287 if (attrCommentLine.contains("@removed")) {
Michael Wrightfeaf99f2016-05-06 17:16:06 +0100288 // Removed attributes are public but hidden from the documentation, so don't emit
289 // them as part of the class documentation.
290 continue;
291 }
292
Adam Lesinski74605cd2016-03-03 15:39:50 -0800293 const ResourceName& attrName = entry.attrRef->name.value();
Adam Lesinski76565542016-03-10 21:55:04 -0800294 styleableComment << "<tr><td>";
295 styleableComment << "<code>{@link #"
296 << entry.fieldName << " "
297 << (!attrName.package.empty()
298 ? attrName.package : mContext->getCompilationPackage())
299 << ":" << attrName.entry
300 << "}</code>";
301 styleableComment << "</td>";
302
303 styleableComment << "<td>";
Adam Lesinski626b3db2016-04-07 13:24:59 -0700304
305 // Only use the comment up until the first '.'. This is to stay compatible with
306 // the way old AAPT did it (presumably to keep it short and to avoid including
307 // annotations like @hide which would affect this Styleable).
Adam Lesinski626b3db2016-04-07 13:24:59 -0700308 auto iter = std::find(attrCommentLine.begin(), attrCommentLine.end(), u'.');
309 if (iter != attrCommentLine.end()) {
310 attrCommentLine = attrCommentLine.substr(
311 0, (iter - attrCommentLine.begin()) + 1);
Adam Lesinski76565542016-03-10 21:55:04 -0800312 }
Adam Lesinski626b3db2016-04-07 13:24:59 -0700313 styleableComment << attrCommentLine << "</td></tr>\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800314 }
315 styleableComment << "</table>\n";
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700316
317 for (const StyleableAttr& entry : sortedAttributes) {
Adam Lesinski626b3db2016-04-07 13:24:59 -0700318 if (!entry.symbol) {
319 continue;
320 }
321
322 if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
323 !entry.symbol->isPublic) {
324 // Don't write entries for non-public attributes.
325 continue;
326 }
Adam Lesinski74605cd2016-03-03 15:39:50 -0800327 styleableComment << "@see #" << entry.fieldName << "\n";
328 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700329
330 styleableArrayDef->getCommentBuilder()->appendComment(styleableComment.str());
Adam Lesinski74605cd2016-03-03 15:39:50 -0800331 }
332
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700333 // Add the ResourceIds to the array member.
334 for (const StyleableAttr& styleableAttr : sortedAttributes) {
335 styleableArrayDef->addElement(
336 styleableAttr.attrRef->id ? styleableAttr.attrRef->id.value() : ResourceId(0));
337 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800338
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700339 // Add the Styleable array to the Styleable class.
340 outStyleableClassDef->addMember(std::move(styleableArrayDef));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800341
342 // Now we emit the indices into the array.
343 for (size_t i = 0; i < attrCount; i++) {
Adam Lesinski76565542016-03-10 21:55:04 -0800344 const StyleableAttr& styleableAttr = sortedAttributes[i];
Adam Lesinski626b3db2016-04-07 13:24:59 -0700345
346 if (!styleableAttr.symbol) {
347 continue;
348 }
349
350 if (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic &&
351 !styleableAttr.symbol->isPublic) {
352 // Don't write entries for non-public attributes.
353 continue;
354 }
355
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700356 StringPiece comment = styleableAttr.attrRef->getComment();
Michael Wrightfeaf99f2016-05-06 17:16:06 +0100357 if (styleableAttr.symbol->attribute && comment.empty()) {
358 comment = styleableAttr.symbol->attribute->getComment();
359 }
360
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700361 if (comment.contains("@removed")) {
Michael Wrightfeaf99f2016-05-06 17:16:06 +0100362 // Removed attributes are public but hidden from the documentation, so don't emit them
363 // as part of the class documentation.
364 continue;
365 }
366
Adam Lesinski76565542016-03-10 21:55:04 -0800367 const ResourceName& attrName = styleableAttr.attrRef->name.value();
368
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700369 StringPiece packageName = attrName.package;
Adam Lesinski76565542016-03-10 21:55:04 -0800370 if (packageName.empty()) {
371 packageName = mContext->getCompilationPackage();
372 }
Adam Lesinski838a6872015-05-01 13:14:05 -0700373
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700374 std::unique_ptr<IntMember> indexMember = util::make_unique<IntMember>(
Adam Lesinski803c7c82016-04-06 16:09:43 -0700375 sortedAttributes[i].fieldName, static_cast<uint32_t>(i));
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700376
377 AnnotationProcessor* attrProcessor = indexMember->getCommentBuilder();
Adam Lesinski76565542016-03-10 21:55:04 -0800378
Adam Lesinski76565542016-03-10 21:55:04 -0800379 if (!comment.empty()) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700380 attrProcessor->appendComment("<p>\n@attr description");
381 attrProcessor->appendComment(comment);
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700382 } else {
Adam Lesinski76565542016-03-10 21:55:04 -0800383 std::stringstream defaultComment;
384 defaultComment
385 << "<p>This symbol is the offset where the "
386 << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
387 << "attribute's value can be found in the "
388 << "{@link #" << className << "} array.";
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700389 attrProcessor->appendComment(defaultComment.str());
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700390 }
391
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700392 attrProcessor->appendNewLine();
Adam Lesinski76565542016-03-10 21:55:04 -0800393
Adam Lesinski626b3db2016-04-07 13:24:59 -0700394 addAttributeFormatDoc(attrProcessor, styleableAttr.symbol->attribute.get());
395 attrProcessor->appendNewLine();
Adam Lesinski76565542016-03-10 21:55:04 -0800396
397 std::stringstream doclavaName;
398 doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700399 attrProcessor->appendComment(doclavaName.str());
400
401 outStyleableClassDef->addMember(std::move(indexMember));
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700402 }
403}
404
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700405bool JavaClassGenerator::addMembersToTypeClass(const StringPiece& packageNameToGenerate,
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700406 const ResourceTablePackage* package,
407 const ResourceTableType* type,
408 ClassDefinition* outTypeClassDef) {
409
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700410 for (const auto& entry : type->entries) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700411 if (skipSymbol(entry->symbolStatus.state)) {
412 continue;
413 }
414
Adam Lesinski64587af2016-02-18 18:33:06 -0800415 ResourceId id;
416 if (package->id && type->id && entry->id) {
417 id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
418 }
Adam Lesinski769de982015-04-10 19:43:55 -0700419
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700420 std::string unmangledPackage;
421 std::string unmangledName = entry->name;
Adam Lesinski769de982015-04-10 19:43:55 -0700422 if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
423 // The entry name was mangled, and we successfully unmangled it.
424 // Check that we want to emit this symbol.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700425 if (package->name != unmangledPackage) {
Adam Lesinski769de982015-04-10 19:43:55 -0700426 // Skip the entry if it doesn't belong to the package we're writing.
427 continue;
428 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800429 } else if (packageNameToGenerate != package->name) {
430 // We are processing a mangled package name,
431 // but this is a non-mangled resource.
432 continue;
Adam Lesinski769de982015-04-10 19:43:55 -0700433 }
434
435 if (!isValidSymbol(unmangledName)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700436 ResourceNameRef resourceName(packageNameToGenerate, type->type, unmangledName);
Adam Lesinski769de982015-04-10 19:43:55 -0700437 std::stringstream err;
438 err << "invalid symbol name '" << resourceName << "'";
439 mError = err.str();
440 return false;
441 }
442
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700443 if (type->type == ResourceType::kStyleable) {
Adam Lesinski769de982015-04-10 19:43:55 -0700444 assert(!entry->values.empty());
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700445
Adam Lesinskib274e352015-11-06 15:14:35 -0800446 const Styleable* styleable = static_cast<const Styleable*>(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800447 entry->values.front()->value.get());
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700448
449 // Comments are handled within this method.
450 addMembersToStyleableClass(packageNameToGenerate, unmangledName, styleable,
451 outTypeClassDef);
Adam Lesinski769de982015-04-10 19:43:55 -0700452 } else {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700453 std::unique_ptr<ResourceMember> resourceMember =
454 util::make_unique<ResourceMember>(transform(unmangledName), id);
455
456 // Build the comments and annotations for this entry.
457 AnnotationProcessor* processor = resourceMember->getCommentBuilder();
458
459 // Add the comments from any <public> tags.
460 if (entry->symbolStatus.state != SymbolState::kUndefined) {
461 processor->appendComment(entry->symbolStatus.comment);
462 }
463
464 // Add the comments from all configurations of this entry.
465 for (const auto& configValue : entry->values) {
466 processor->appendComment(configValue->value->getComment());
467 }
468
469 // If this is an Attribute, append the format Javadoc.
470 if (!entry->values.empty()) {
471 if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
472 // We list out the available values for the given attribute.
473 addAttributeFormatDoc(processor, attr);
474 }
475 }
476
477 outTypeClassDef->addMember(std::move(resourceMember));
Adam Lesinski769de982015-04-10 19:43:55 -0700478 }
479 }
480 return true;
481}
482
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700483bool JavaClassGenerator::generate(const StringPiece& packageNameToGenerate, std::ostream* out) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700484 return generate(packageNameToGenerate, packageNameToGenerate, out);
485}
486
Adam Lesinski3524a232016-04-01 19:19:24 -0700487static void appendJavaDocAnnotations(const std::vector<std::string>& annotations,
488 AnnotationProcessor* processor) {
489 for (const std::string& annotation : annotations) {
490 std::string properAnnotation = "@";
491 properAnnotation += annotation;
492 processor->appendComment(properAnnotation);
493 }
494}
495
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700496bool JavaClassGenerator::generate(const StringPiece& packageNameToGenerate,
497 const StringPiece& outPackageName, std::ostream* out) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800498
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700499 ClassDefinition rClass("R", ClassQualifier::None, true);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800500
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700501 for (const auto& package : mTable->packages) {
502 for (const auto& type : package->types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700503 if (type->type == ResourceType::kAttrPrivate) {
Adam Lesinskib274e352015-11-06 15:14:35 -0800504 continue;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700505 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800506
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700507 const bool forceCreationIfEmpty =
Adam Lesinskib274e352015-11-06 15:14:35 -0800508 (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700509
510 std::unique_ptr<ClassDefinition> classDef = util::make_unique<ClassDefinition>(
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700511 toString(type->type), ClassQualifier::Static, forceCreationIfEmpty);
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700512
513 bool result = addMembersToTypeClass(packageNameToGenerate, package.get(), type.get(),
514 classDef.get());
Adam Lesinskib274e352015-11-06 15:14:35 -0800515 if (!result) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700516 return false;
517 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800518
519 if (type->type == ResourceType::kAttr) {
520 // Also include private attributes in this same class.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800521 ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
522 if (privType) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700523 result = addMembersToTypeClass(packageNameToGenerate, package.get(), privType,
524 classDef.get());
Adam Lesinskib274e352015-11-06 15:14:35 -0800525 if (!result) {
526 return false;
527 }
528 }
529 }
530
Adam Lesinskib274e352015-11-06 15:14:35 -0800531 if (type->type == ResourceType::kStyleable &&
532 mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
533 // When generating a public R class, we don't want Styleable to be part of the API.
534 // It is only emitted for documentation purposes.
Adam Lesinski3524a232016-04-01 19:19:24 -0700535 classDef->getCommentBuilder()->appendComment("@doconly");
Adam Lesinskib274e352015-11-06 15:14:35 -0800536 }
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700537
Adam Lesinski3524a232016-04-01 19:19:24 -0700538 appendJavaDocAnnotations(mOptions.javadocAnnotations, classDef->getCommentBuilder());
539
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700540 rClass.addMember(std::move(classDef));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800541 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800542 }
543
Adam Lesinski3524a232016-04-01 19:19:24 -0700544 appendJavaDocAnnotations(mOptions.javadocAnnotations, rClass.getCommentBuilder());
545
Adam Lesinskid0f116b2016-07-08 15:00:32 -0700546 if (!ClassDefinition::writeJavaFile(&rClass, outPackageName, mOptions.useFinal, out)) {
Adam Lesinski6cbfb1d2016-03-31 13:33:02 -0700547 return false;
548 }
549
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700550 out->flush();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800551 return true;
552}
553
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800554} // namespace aapt