blob: f312d75ab1e9bc5b66bbe4ab2e8f85579f86c750 [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
17#include "Resource.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080018#include "ResourceValues.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070019#include "ValueVisitor.h"
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080020
Adam Lesinskie78fd612015-10-22 12:48:43 -070021#include "util/Util.h"
22#include "flatten/ResourceTypeExtensions.h"
23
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080024#include <androidfw/ResourceTypes.h>
25#include <limits>
26
27namespace aapt {
28
Adam Lesinski1ab598f2015-08-14 14:26:04 -070029template <typename Derived>
30void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
31 visitor->visit(static_cast<Derived*>(this));
32}
33
34template <typename Derived>
35void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
36 visitor->visit(static_cast<Derived*>(this));
37}
38
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080039bool Value::isWeak() const {
40 return false;
41}
42
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080043RawString::RawString(const StringPool::Ref& ref) : value(ref) {
44}
45
Adam Lesinski769de982015-04-10 19:43:55 -070046RawString* RawString::clone(StringPool* newPool) const {
47 return new RawString(newPool->makeRef(*value));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080048}
49
Adam Lesinski1ab598f2015-08-14 14:26:04 -070050bool RawString::flatten(android::Res_value* outValue) const {
51 outValue->dataType = ExtendedTypes::TYPE_RAW_STRING;
52 outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080053 return true;
54}
55
Adam Lesinski1ab598f2015-08-14 14:26:04 -070056void RawString::print(std::ostream* out) const {
57 *out << "(raw string) " << *value;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080058}
59
60Reference::Reference() : referenceType(Reference::Type::kResource) {
61}
62
63Reference::Reference(const ResourceNameRef& n, Type t) :
64 name(n.toResourceName()), referenceType(t) {
65}
66
67Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
68}
69
Adam Lesinski1ab598f2015-08-14 14:26:04 -070070bool Reference::flatten(android::Res_value* outValue) const {
71 outValue->dataType = (referenceType == Reference::Type::kResource)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080072 ? android::Res_value::TYPE_REFERENCE
73 : android::Res_value::TYPE_ATTRIBUTE;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070074 outValue->data = util::hostToDevice32(id ? id.value().id : 0);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080075 return true;
76}
77
Adam Lesinski769de982015-04-10 19:43:55 -070078Reference* Reference::clone(StringPool* /*newPool*/) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080079 Reference* ref = new Reference();
80 ref->referenceType = referenceType;
81 ref->name = name;
82 ref->id = id;
83 return ref;
84}
85
Adam Lesinski1ab598f2015-08-14 14:26:04 -070086void Reference::print(std::ostream* out) const {
87 *out << "(reference) ";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080088 if (referenceType == Reference::Type::kResource) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070089 *out << "@";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080090 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070091 *out << "?";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080092 }
93
Adam Lesinski1ab598f2015-08-14 14:26:04 -070094 if (name) {
95 *out << name.value();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -080096 }
97
Adam Lesinski1ab598f2015-08-14 14:26:04 -070098 if (id && !Res_INTERNALID(id.value().id)) {
99 *out << " " << id.value();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800100 }
101}
102
103bool Id::isWeak() const {
104 return true;
105}
106
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700107bool Id::flatten(android::Res_value* out) const {
108 out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
109 out->data = util::hostToDevice32(0);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800110 return true;
111}
112
Adam Lesinski769de982015-04-10 19:43:55 -0700113Id* Id::clone(StringPool* /*newPool*/) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800114 return new Id();
115}
116
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700117void Id::print(std::ostream* out) const {
118 *out << "(id)";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800119}
120
121String::String(const StringPool::Ref& ref) : value(ref) {
122}
123
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700124bool String::flatten(android::Res_value* outValue) const {
125 // Verify that our StringPool index is within encode-able limits.
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800126 if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
127 return false;
128 }
129
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700130 outValue->dataType = android::Res_value::TYPE_STRING;
131 outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800132 return true;
133}
134
Adam Lesinski769de982015-04-10 19:43:55 -0700135String* String::clone(StringPool* newPool) const {
136 return new String(newPool->makeRef(*value));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800137}
138
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700139void String::print(std::ostream* out) const {
140 *out << "(string) \"" << *value << "\"";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800141}
142
143StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
144}
145
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700146bool StyledString::flatten(android::Res_value* outValue) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800147 if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
148 return false;
149 }
150
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700151 outValue->dataType = android::Res_value::TYPE_STRING;
152 outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800153 return true;
154}
155
Adam Lesinski769de982015-04-10 19:43:55 -0700156StyledString* StyledString::clone(StringPool* newPool) const {
157 return new StyledString(newPool->makeRef(value));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800158}
159
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700160void StyledString::print(std::ostream* out) const {
161 *out << "(styled string) \"" << *value->str << "\"";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800162}
163
164FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
165}
166
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700167bool FileReference::flatten(android::Res_value* outValue) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800168 if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
169 return false;
170 }
171
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700172 outValue->dataType = android::Res_value::TYPE_STRING;
173 outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800174 return true;
175}
176
Adam Lesinski769de982015-04-10 19:43:55 -0700177FileReference* FileReference::clone(StringPool* newPool) const {
178 return new FileReference(newPool->makeRef(*path));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800179}
180
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700181void FileReference::print(std::ostream* out) const {
182 *out << "(file) " << *path;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800183}
184
185BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
186}
187
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700188BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
189 value.dataType = dataType;
190 value.data = data;
191}
192
193bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
194 outValue->dataType = value.dataType;
195 outValue->data = util::hostToDevice32(value.data);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800196 return true;
197}
198
Adam Lesinski769de982015-04-10 19:43:55 -0700199BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800200 return new BinaryPrimitive(value);
201}
202
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700203void BinaryPrimitive::print(std::ostream* out) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800204 switch (value.dataType) {
205 case android::Res_value::TYPE_NULL:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700206 *out << "(null)";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800207 break;
208 case android::Res_value::TYPE_INT_DEC:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700209 *out << "(integer) " << value.data;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800210 break;
211 case android::Res_value::TYPE_INT_HEX:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700212 *out << "(integer) " << std::hex << value.data << std::dec;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800213 break;
214 case android::Res_value::TYPE_INT_BOOLEAN:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700215 *out << "(boolean) " << (value.data != 0 ? "true" : "false");
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800216 break;
217 case android::Res_value::TYPE_INT_COLOR_ARGB8:
218 case android::Res_value::TYPE_INT_COLOR_RGB8:
219 case android::Res_value::TYPE_INT_COLOR_ARGB4:
220 case android::Res_value::TYPE_INT_COLOR_RGB4:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700221 *out << "(color) #" << std::hex << value.data << std::dec;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800222 break;
223 default:
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700224 *out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
225 << std::hex << value.data << std::dec;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800226 break;
227 }
228}
229
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800230Attribute::Attribute(bool w, uint32_t t) : weak(w), typeMask(t) {
231}
232
233bool Attribute::isWeak() const {
234 return weak;
235}
236
Adam Lesinski769de982015-04-10 19:43:55 -0700237Attribute* Attribute::clone(StringPool* /*newPool*/) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800238 Attribute* attr = new Attribute(weak);
239 attr->typeMask = typeMask;
240 std::copy(symbols.begin(), symbols.end(), std::back_inserter(attr->symbols));
241 return attr;
242}
243
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700244void Attribute::printMask(std::ostream* out) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800245 if (typeMask == android::ResTable_map::TYPE_ANY) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700246 *out << "any";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800247 return;
248 }
249
250 bool set = false;
251 if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
252 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800253 set = true;
254 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700255 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800256 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700257 *out << "reference";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800258 }
259
260 if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
261 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800262 set = true;
263 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700264 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800265 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700266 *out << "string";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800267 }
268
269 if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
270 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800271 set = true;
272 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700273 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800274 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700275 *out << "integer";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800276 }
277
278 if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
279 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800280 set = true;
281 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700282 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800283 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700284 *out << "boolean";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800285 }
286
287 if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
288 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800289 set = true;
290 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700291 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800292 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700293 *out << "color";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800294 }
295
296 if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
297 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800298 set = true;
299 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700300 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800301 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700302 *out << "float";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800303 }
304
305 if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
306 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800307 set = true;
308 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700309 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800310 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700311 *out << "dimension";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800312 }
313
314 if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
315 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800316 set = true;
317 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700318 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800319 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700320 *out << "fraction";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800321 }
322
323 if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
324 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800325 set = true;
326 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700327 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800328 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700329 *out << "enum";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800330 }
331
332 if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
333 if (!set) {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800334 set = true;
335 } else {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700336 *out << "|";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800337 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700338 *out << "flags";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800339 }
Adam Lesinski330edcd2015-05-04 17:40:56 -0700340}
341
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700342void Attribute::print(std::ostream* out) const {
343 *out << "(attr) ";
Adam Lesinski330edcd2015-05-04 17:40:56 -0700344 printMask(out);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800345
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700346 if (!symbols.empty()) {
347 *out << " ["
348 << util::joiner(symbols.begin(), symbols.end(), ", ")
349 << "]";
350 }
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800351
352 if (weak) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700353 *out << " [weak]";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800354 }
355}
356
Adam Lesinski769de982015-04-10 19:43:55 -0700357Style* Style::clone(StringPool* newPool) const {
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700358 Style* style = new Style();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800359 style->parent = parent;
Adam Lesinskibdaa0922015-05-08 20:16:23 -0700360 style->parentInferred = parentInferred;
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800361 for (auto& entry : entries) {
362 style->entries.push_back(Entry{
363 entry.key,
Adam Lesinski769de982015-04-10 19:43:55 -0700364 std::unique_ptr<Item>(entry.value->clone(newPool))
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800365 });
366 }
367 return style;
368}
369
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700370void Style::print(std::ostream* out) const {
371 *out << "(style) ";
372 if (parent && parent.value().name) {
373 *out << parent.value().name.value();
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800374 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700375 *out << " ["
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800376 << util::joiner(entries.begin(), entries.end(), ", ")
377 << "]";
378}
379
380static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700381 if (value.key.name) {
382 out << value.key.name.value();
383 } else {
384 out << "???";
385 }
386 out << " = ";
387 value.value->print(&out);
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800388 return out;
389}
390
Adam Lesinski769de982015-04-10 19:43:55 -0700391Array* Array::clone(StringPool* newPool) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800392 Array* array = new Array();
393 for (auto& item : items) {
Adam Lesinski769de982015-04-10 19:43:55 -0700394 array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800395 }
396 return array;
397}
398
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700399void Array::print(std::ostream* out) const {
400 *out << "(array) ["
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800401 << util::joiner(items.begin(), items.end(), ", ")
402 << "]";
403}
404
Adam Lesinski769de982015-04-10 19:43:55 -0700405Plural* Plural::clone(StringPool* newPool) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800406 Plural* p = new Plural();
407 const size_t count = values.size();
408 for (size_t i = 0; i < count; i++) {
409 if (values[i]) {
Adam Lesinski769de982015-04-10 19:43:55 -0700410 p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800411 }
412 }
413 return p;
414}
415
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700416void Plural::print(std::ostream* out) const {
417 *out << "(plural)";
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800418}
419
420static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
421 return out << *item;
422}
423
Adam Lesinski769de982015-04-10 19:43:55 -0700424Styleable* Styleable::clone(StringPool* /*newPool*/) const {
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800425 Styleable* styleable = new Styleable();
426 std::copy(entries.begin(), entries.end(), std::back_inserter(styleable->entries));
427 return styleable;
428}
429
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700430void Styleable::print(std::ostream* out) const {
431 *out << "(styleable) " << " ["
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800432 << util::joiner(entries.begin(), entries.end(), ", ")
433 << "]";
434}
435
436} // namespace aapt