blob: 01330dc6b5a16defd248b51992ad38d5194b1772 [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"
22
Adam Lesinskica5638f2015-10-21 14:42:43 -070023#include "java/AnnotationProcessor.h"
Adam Lesinskib274e352015-11-06 15:14:35 -080024#include "java/ClassDefinitionWriter.h"
Adam Lesinskica5638f2015-10-21 14:42:43 -070025#include "java/JavaClassGenerator.h"
Adam Lesinski76565542016-03-10 21:55:04 -080026#include "process/SymbolTable.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070027#include "util/StringPiece.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080028
Adam Lesinskica2fc352015-04-03 12:08:26 -070029#include <algorithm>
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080030#include <ostream>
31#include <set>
32#include <sstream>
33#include <tuple>
34
35namespace aapt {
36
Adam Lesinski76565542016-03-10 21:55:04 -080037JavaClassGenerator::JavaClassGenerator(IAaptContext* context, ResourceTable* table,
38 const JavaClassGeneratorOptions& options) :
39 mContext(context), mTable(table), mOptions(options) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080040}
41
Adam Lesinski1ab598f2015-08-14 14:26:04 -070042static void generateHeader(const StringPiece16& packageNameToGenerate, std::ostream* out) {
43 *out << "/* AUTO-GENERATED FILE. DO NOT MODIFY.\n"
44 " *\n"
45 " * This class was automatically generated by the\n"
46 " * aapt tool from the resource data it found. It\n"
47 " * should not be modified by hand.\n"
48 " */\n\n"
49 "package " << packageNameToGenerate << ";\n\n";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080050}
51
52static const std::set<StringPiece16> sJavaIdentifiers = {
53 u"abstract", u"assert", u"boolean", u"break", u"byte",
54 u"case", u"catch", u"char", u"class", u"const", u"continue",
55 u"default", u"do", u"double", u"else", u"enum", u"extends",
56 u"final", u"finally", u"float", u"for", u"goto", u"if",
57 u"implements", u"import", u"instanceof", u"int", u"interface",
58 u"long", u"native", u"new", u"package", u"private", u"protected",
59 u"public", u"return", u"short", u"static", u"strictfp", u"super",
60 u"switch", u"synchronized", u"this", u"throw", u"throws",
61 u"transient", u"try", u"void", u"volatile", u"while", u"true",
62 u"false", u"null"
63};
64
65static bool isValidSymbol(const StringPiece16& symbol) {
66 return sJavaIdentifiers.find(symbol) == sJavaIdentifiers.end();
67}
68
69/*
70 * Java symbols can not contain . or -, but those are valid in a resource name.
71 * Replace those with '_'.
72 */
Adam Lesinski74605cd2016-03-03 15:39:50 -080073static std::string transform(const StringPiece16& symbol) {
74 std::string output = util::utf16ToUtf8(symbol);
75 for (char& c : output) {
76 if (c == '.' || c == '-') {
77 c = '_';
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080078 }
79 }
80 return output;
81}
82
Adam Lesinski74605cd2016-03-03 15:39:50 -080083/**
84 * Transforms an attribute in a styleable to the Java field name:
85 *
86 * <declare-styleable name="Foo">
87 * <attr name="android:bar" />
88 * <attr name="bar" />
89 * </declare-styleable>
90 *
91 * Foo_android_bar
92 * Foo_bar
93 */
94static std::string transformNestedAttr(const ResourceNameRef& attrName,
95 const std::string& styleableClassName,
96 const StringPiece16& packageNameToGenerate) {
97 std::string output = styleableClassName;
98
99 // We may reference IDs from other packages, so prefix the entry name with
100 // the package.
101 if (!attrName.package.empty() && packageNameToGenerate != attrName.package) {
102 output += "_" + transform(attrName.package);
103 }
104 output += "_" + transform(attrName.entry);
105 return output;
106}
107
Adam Lesinski76565542016-03-10 21:55:04 -0800108static void addAttributeFormatDoc(AnnotationProcessor* processor, Attribute* attr) {
109 const uint32_t typeMask = attr->typeMask;
110 if (typeMask & android::ResTable_map::TYPE_REFERENCE) {
111 processor->appendComment(
112 "<p>May be a reference to another resource, in the form\n"
113 "\"<code>@[+][<i>package</i>:]<i>type</i>/<i>name</i></code>\" or a theme\n"
114 "attribute in the form\n"
115 "\"<code>?[<i>package</i>:]<i>type</i>/<i>name</i></code>\".");
116 }
117
118 if (typeMask & android::ResTable_map::TYPE_STRING) {
119 processor->appendComment(
120 "<p>May be a string value, using '\\\\;' to escape characters such as\n"
121 "'\\\\n' or '\\\\uxxxx' for a unicode character;");
122 }
123
124 if (typeMask & android::ResTable_map::TYPE_INTEGER) {
125 processor->appendComment("<p>May be an integer value, such as \"<code>100</code>\".");
126 }
127
128 if (typeMask & android::ResTable_map::TYPE_BOOLEAN) {
129 processor->appendComment(
130 "<p>May be a boolean value, such as \"<code>true</code>\" or\n"
131 "\"<code>false</code>\".");
132 }
133
134 if (typeMask & android::ResTable_map::TYPE_COLOR) {
135 processor->appendComment(
136 "<p>May be a color value, in the form of \"<code>#<i>rgb</i></code>\",\n"
137 "\"<code>#<i>argb</i></code>\", \"<code>#<i>rrggbb</i></code\", or \n"
138 "\"<code>#<i>aarrggbb</i></code>\".");
139 }
140
141 if (typeMask & android::ResTable_map::TYPE_FLOAT) {
142 processor->appendComment(
143 "<p>May be a floating point value, such as \"<code>1.2</code>\".");
144 }
145
146 if (typeMask & android::ResTable_map::TYPE_DIMENSION) {
147 processor->appendComment(
148 "<p>May be a dimension value, which is a floating point number appended with a\n"
149 "unit such as \"<code>14.5sp</code>\".\n"
150 "Available units are: px (pixels), dp (density-independent pixels),\n"
151 "sp (scaled pixels based on preferred font size), in (inches), and\n"
152 "mm (millimeters).");
153 }
154
155 if (typeMask & android::ResTable_map::TYPE_FRACTION) {
156 processor->appendComment(
157 "<p>May be a fractional value, which is a floating point number appended with\n"
158 "either % or %p, such as \"<code>14.5%</code>\".\n"
159 "The % suffix always means a percentage of the base size;\n"
160 "the optional %p suffix provides a size relative to some parent container.");
161 }
162
163 if (typeMask & (android::ResTable_map::TYPE_FLAGS | android::ResTable_map::TYPE_ENUM)) {
164 if (typeMask & android::ResTable_map::TYPE_FLAGS) {
165 processor->appendComment(
166 "<p>Must be one or more (separated by '|') of the following "
167 "constant values.</p>");
168 } else {
169 processor->appendComment("<p>Must be one of the following constant values.</p>");
170 }
171
172 processor->appendComment("<table>\n<colgroup align=\"left\" />\n"
173 "<colgroup align=\"left\" />\n"
174 "<colgroup align=\"left\" />\n"
175 "<tr><th>Constant</th><th>Value</th><th>Description</th></tr>\n");
176 for (const Attribute::Symbol& symbol : attr->symbols) {
177 std::stringstream line;
178 line << "<tr><td>" << symbol.symbol.name.value().entry << "</td>"
179 << "<td>" << std::hex << symbol.value << std::dec << "</td>"
180 << "<td>" << util::trimWhitespace(symbol.symbol.getComment()) << "</td></tr>";
181 processor->appendComment(line.str());
182 }
183 processor->appendComment("</table>");
184 }
185}
186
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700187bool JavaClassGenerator::skipSymbol(SymbolState state) {
188 switch (mOptions.types) {
189 case JavaClassGeneratorOptions::SymbolTypes::kAll:
190 return false;
191 case JavaClassGeneratorOptions::SymbolTypes::kPublicPrivate:
192 return state == SymbolState::kUndefined;
193 case JavaClassGeneratorOptions::SymbolTypes::kPublic:
194 return state != SymbolState::kPublic;
195 }
196 return true;
197}
198
Adam Lesinski74605cd2016-03-03 15:39:50 -0800199struct StyleableAttr {
200 const Reference* attrRef;
Adam Lesinski76565542016-03-10 21:55:04 -0800201 std::shared_ptr<Attribute> attribute;
Adam Lesinski74605cd2016-03-03 15:39:50 -0800202 std::string fieldName;
203};
204
205static bool lessStyleableAttr(const StyleableAttr& lhs, const StyleableAttr& rhs) {
206 const ResourceId lhsId = lhs.attrRef->id ? lhs.attrRef->id.value() : ResourceId(0);
207 const ResourceId rhsId = rhs.attrRef->id ? rhs.attrRef->id.value() : ResourceId(0);
208 if (lhsId < rhsId) {
209 return true;
210 } else if (lhsId > rhsId) {
211 return false;
212 } else {
213 return lhs.attrRef->name.value() < rhs.attrRef->name.value();
214 }
215}
216
Adam Lesinskib274e352015-11-06 15:14:35 -0800217void JavaClassGenerator::writeStyleableEntryForClass(ClassDefinitionWriter* outClassDef,
218 AnnotationProcessor* processor,
219 const StringPiece16& packageNameToGenerate,
220 const std::u16string& entryName,
221 const Styleable* styleable) {
Adam Lesinski74605cd2016-03-03 15:39:50 -0800222 const std::string className = transform(entryName);
223
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800224 // This must be sorted by resource ID.
Adam Lesinski74605cd2016-03-03 15:39:50 -0800225 std::vector<StyleableAttr> sortedAttributes;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700226 sortedAttributes.reserve(styleable->entries.size());
227 for (const auto& attr : styleable->entries) {
Adam Lesinski330edcd2015-05-04 17:40:56 -0700228 // If we are not encoding final attributes, the styleable entry may have no ID
229 // if we are building a static library.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700230 assert((!mOptions.useFinal || attr.id) && "no ID set for Styleable entry");
231 assert(attr.name && "no name set for Styleable entry");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800232
Adam Lesinski76565542016-03-10 21:55:04 -0800233 StyleableAttr styleableAttr = {};
234 styleableAttr.attrRef = &attr;
235 styleableAttr.fieldName = transformNestedAttr(attr.name.value(), className,
236 packageNameToGenerate);
237
238 Reference mangledReference;
239 mangledReference.id = attr.id;
240 mangledReference.name = attr.name;
241 if (mangledReference.name.value().package.empty()) {
242 mangledReference.name.value().package = mContext->getCompilationPackage();
243 }
244
245 if (Maybe<ResourceName> mangledName =
246 mContext->getNameMangler()->mangleName(mangledReference.name.value())) {
247 mangledReference.name = mangledName;
248 }
249
250 const SymbolTable::Symbol* symbol = mContext->getExternalSymbols()->findByReference(
251 mangledReference);
252 if (symbol) {
253 styleableAttr.attribute = symbol->attribute;
254 }
255 sortedAttributes.push_back(std::move(styleableAttr));
Adam Lesinski74605cd2016-03-03 15:39:50 -0800256 }
257 std::sort(sortedAttributes.begin(), sortedAttributes.end(), lessStyleableAttr);
258
259 const size_t attrCount = sortedAttributes.size();
260
261 if (attrCount > 0) {
262 // Build the comment string for the Styleable. It includes details about the
263 // child attributes.
264 std::stringstream styleableComment;
Adam Lesinski76565542016-03-10 21:55:04 -0800265 if (!styleable->getComment().empty()) {
266 styleableComment << styleable->getComment() << "\n";
267 } else {
268 styleableComment << "Attributes that can be used with a " << className << ".\n";
269 }
270 styleableComment <<
271 "<p>Includes the following attributes:</p>\n"
272 "<table>\n"
Adam Lesinski74605cd2016-03-03 15:39:50 -0800273 "<colgroup align=\"left\" />\n"
Adam Lesinski76565542016-03-10 21:55:04 -0800274 "<colgroup align=\"left\" />\n"
Adam Lesinski74605cd2016-03-03 15:39:50 -0800275 "<tr><th>Attribute</th><th>Description</th></tr>\n";
Adam Lesinski76565542016-03-10 21:55:04 -0800276
Adam Lesinski74605cd2016-03-03 15:39:50 -0800277 for (const auto& entry : sortedAttributes) {
278 const ResourceName& attrName = entry.attrRef->name.value();
Adam Lesinski76565542016-03-10 21:55:04 -0800279 styleableComment << "<tr><td>";
280 styleableComment << "<code>{@link #"
281 << entry.fieldName << " "
282 << (!attrName.package.empty()
283 ? attrName.package : mContext->getCompilationPackage())
284 << ":" << attrName.entry
285 << "}</code>";
286 styleableComment << "</td>";
287
288 styleableComment << "<td>";
289 if (entry.attribute) {
290 styleableComment << entry.attribute->getComment();
291 }
292 styleableComment << "</td></tr>\n";
Adam Lesinski74605cd2016-03-03 15:39:50 -0800293 }
294 styleableComment << "</table>\n";
295 for (const auto& entry : sortedAttributes) {
296 styleableComment << "@see #" << entry.fieldName << "\n";
297 }
298 processor->appendComment(styleableComment.str());
299 }
300
301 auto accessorFunc = [](const StyleableAttr& a) -> ResourceId {
302 return a.attrRef->id ? a.attrRef->id.value() : ResourceId(0);
Adam Lesinskib274e352015-11-06 15:14:35 -0800303 };
304
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800305 // First we emit the array containing the IDs of each attribute.
Adam Lesinski74605cd2016-03-03 15:39:50 -0800306 outClassDef->addArrayMember(className, processor,
Adam Lesinskib274e352015-11-06 15:14:35 -0800307 sortedAttributes.begin(),
308 sortedAttributes.end(),
309 accessorFunc);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800310
311 // Now we emit the indices into the array.
312 for (size_t i = 0; i < attrCount; i++) {
Adam Lesinski76565542016-03-10 21:55:04 -0800313 const StyleableAttr& styleableAttr = sortedAttributes[i];
314 const ResourceName& attrName = styleableAttr.attrRef->name.value();
315
316 StringPiece16 packageName = attrName.package;
317 if (packageName.empty()) {
318 packageName = mContext->getCompilationPackage();
319 }
Adam Lesinski838a6872015-05-01 13:14:05 -0700320
Adam Lesinski74605cd2016-03-03 15:39:50 -0800321 AnnotationProcessor attrProcessor;
Adam Lesinski76565542016-03-10 21:55:04 -0800322
323 StringPiece16 comment = styleableAttr.attrRef->getComment();
324 if (styleableAttr.attribute && comment.empty()) {
325 comment = styleableAttr.attribute->getComment();
Adam Lesinski838a6872015-05-01 13:14:05 -0700326 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800327
Adam Lesinski76565542016-03-10 21:55:04 -0800328 if (!comment.empty()) {
329 attrProcessor.appendComment("<p>\n@attr description");
330 attrProcessor.appendComment(comment);
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700331 } else {
Adam Lesinski76565542016-03-10 21:55:04 -0800332 std::stringstream defaultComment;
333 defaultComment
334 << "<p>This symbol is the offset where the "
335 << "{@link " << packageName << ".R.attr#" << transform(attrName.entry) << "}\n"
336 << "attribute's value can be found in the "
337 << "{@link #" << className << "} array.";
338 attrProcessor.appendComment(defaultComment.str());
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700339 }
340
Adam Lesinski76565542016-03-10 21:55:04 -0800341 attrProcessor.appendNewLine();
342
343 if (styleableAttr.attribute) {
344 addAttributeFormatDoc(&attrProcessor, styleableAttr.attribute.get());
345 attrProcessor.appendNewLine();
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700346 }
Adam Lesinski76565542016-03-10 21:55:04 -0800347
348 std::stringstream doclavaName;
349 doclavaName << "@attr name " << packageName << ":" << attrName.entry;;
350 attrProcessor.appendComment(doclavaName.str());
351 outClassDef->addIntMember(sortedAttributes[i].fieldName, &attrProcessor, i);
Adam Lesinski3b4cd942015-10-30 16:31:42 -0700352 }
353}
354
Adam Lesinskib274e352015-11-06 15:14:35 -0800355bool JavaClassGenerator::writeEntriesForClass(ClassDefinitionWriter* outClassDef,
356 const StringPiece16& packageNameToGenerate,
357 const ResourceTablePackage* package,
358 const ResourceTableType* type) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700359 for (const auto& entry : type->entries) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700360 if (skipSymbol(entry->symbolStatus.state)) {
361 continue;
362 }
363
Adam Lesinski64587af2016-02-18 18:33:06 -0800364 ResourceId id;
365 if (package->id && type->id && entry->id) {
366 id = ResourceId(package->id.value(), type->id.value(), entry->id.value());
367 }
Adam Lesinski769de982015-04-10 19:43:55 -0700368
Adam Lesinskib274e352015-11-06 15:14:35 -0800369 std::u16string unmangledPackage;
370 std::u16string unmangledName = entry->name;
Adam Lesinski769de982015-04-10 19:43:55 -0700371 if (NameMangler::unmangle(&unmangledName, &unmangledPackage)) {
372 // The entry name was mangled, and we successfully unmangled it.
373 // Check that we want to emit this symbol.
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700374 if (package->name != unmangledPackage) {
Adam Lesinski769de982015-04-10 19:43:55 -0700375 // Skip the entry if it doesn't belong to the package we're writing.
376 continue;
377 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800378 } else if (packageNameToGenerate != package->name) {
379 // We are processing a mangled package name,
380 // but this is a non-mangled resource.
381 continue;
Adam Lesinski769de982015-04-10 19:43:55 -0700382 }
383
384 if (!isValidSymbol(unmangledName)) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700385 ResourceNameRef resourceName(packageNameToGenerate, type->type, unmangledName);
Adam Lesinski769de982015-04-10 19:43:55 -0700386 std::stringstream err;
387 err << "invalid symbol name '" << resourceName << "'";
388 mError = err.str();
389 return false;
390 }
391
Adam Lesinskib274e352015-11-06 15:14:35 -0800392 // Build the comments and annotations for this entry.
393
394 AnnotationProcessor processor;
395 if (entry->symbolStatus.state != SymbolState::kUndefined) {
396 processor.appendComment(entry->symbolStatus.comment);
397 }
398
399 for (const auto& configValue : entry->values) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800400 processor.appendComment(configValue->value->getComment());
Adam Lesinskib274e352015-11-06 15:14:35 -0800401 }
402
403 // If this is an Attribute, append the format Javadoc.
404 if (!entry->values.empty()) {
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800405 if (Attribute* attr = valueCast<Attribute>(entry->values.front()->value.get())) {
Adam Lesinskib274e352015-11-06 15:14:35 -0800406 // We list out the available values for the given attribute.
407 addAttributeFormatDoc(&processor, attr);
408 }
409 }
410
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700411 if (type->type == ResourceType::kStyleable) {
Adam Lesinski769de982015-04-10 19:43:55 -0700412 assert(!entry->values.empty());
Adam Lesinskib274e352015-11-06 15:14:35 -0800413 const Styleable* styleable = static_cast<const Styleable*>(
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800414 entry->values.front()->value.get());
Adam Lesinskib274e352015-11-06 15:14:35 -0800415 writeStyleableEntryForClass(outClassDef, &processor, packageNameToGenerate,
416 unmangledName, styleable);
Adam Lesinski769de982015-04-10 19:43:55 -0700417 } else {
Adam Lesinskib274e352015-11-06 15:14:35 -0800418 outClassDef->addResourceMember(transform(unmangledName), &processor, id);
Adam Lesinski769de982015-04-10 19:43:55 -0700419 }
420 }
421 return true;
422}
423
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700424bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate, std::ostream* out) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700425 return generate(packageNameToGenerate, packageNameToGenerate, out);
426}
427
428bool JavaClassGenerator::generate(const StringPiece16& packageNameToGenerate,
429 const StringPiece16& outPackageName, std::ostream* out) {
430 generateHeader(outPackageName, out);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800431
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700432 *out << "public final class R {\n";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800433
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700434 for (const auto& package : mTable->packages) {
435 for (const auto& type : package->types) {
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700436 if (type->type == ResourceType::kAttrPrivate) {
Adam Lesinskib274e352015-11-06 15:14:35 -0800437 continue;
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700438 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800439
440 ClassDefinitionWriterOptions classOptions;
441 classOptions.useFinalQualifier = mOptions.useFinal;
442 classOptions.forceCreationIfEmpty =
443 (mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic);
444 ClassDefinitionWriter classDef(toString(type->type), classOptions);
445 bool result = writeEntriesForClass(&classDef, packageNameToGenerate,
446 package.get(), type.get());
447 if (!result) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700448 return false;
449 }
Adam Lesinskib274e352015-11-06 15:14:35 -0800450
451 if (type->type == ResourceType::kAttr) {
452 // Also include private attributes in this same class.
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800453 ResourceTableType* privType = package->findType(ResourceType::kAttrPrivate);
454 if (privType) {
Adam Lesinskib274e352015-11-06 15:14:35 -0800455 result = writeEntriesForClass(&classDef, packageNameToGenerate,
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -0800456 package.get(), privType);
Adam Lesinskib274e352015-11-06 15:14:35 -0800457 if (!result) {
458 return false;
459 }
460 }
461 }
462
463 AnnotationProcessor processor;
464 if (type->type == ResourceType::kStyleable &&
465 mOptions.types == JavaClassGeneratorOptions::SymbolTypes::kPublic) {
466 // When generating a public R class, we don't want Styleable to be part of the API.
467 // It is only emitted for documentation purposes.
468 processor.appendComment("@doconly");
469 }
470 classDef.writeToStream(out, " ", &processor);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800471 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800472 }
473
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700474 *out << "}\n";
475 out->flush();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800476 return true;
477}
478
Adam Lesinski9e10ac72015-10-16 14:37:48 -0700479
480
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800481} // namespace aapt