2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include "ResourceUtils.h"
19 #include "ResourceValues.h"
20 #include "ValueVisitor.h"
22 #include "util/Util.h"
24 #include <androidfw/ResourceTypes.h>
29 template <typename Derived>
30 void BaseValue<Derived>::accept(RawValueVisitor* visitor) {
31 visitor->visit(static_cast<Derived*>(this));
34 template <typename Derived>
35 void BaseItem<Derived>::accept(RawValueVisitor* visitor) {
36 visitor->visit(static_cast<Derived*>(this));
39 RawString::RawString(const StringPool::Ref& ref) : value(ref) {
42 bool RawString::equals(const Value* value) const {
43 const RawString* other = valueCast<RawString>(value);
47 return *this->value == *other->value;
50 RawString* RawString::clone(StringPool* newPool) const {
51 RawString* rs = new RawString(newPool->makeRef(*value));
52 rs->mComment = mComment;
53 rs->mSource = mSource;
57 bool RawString::flatten(android::Res_value* outValue) const {
58 outValue->dataType = android::Res_value::TYPE_STRING;
59 outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
63 void RawString::print(std::ostream* out) const {
64 *out << "(raw string) " << *value;
67 Reference::Reference() : referenceType(Reference::Type::kResource) {
70 Reference::Reference(const ResourceNameRef& n, Type t) :
71 name(n.toResourceName()), referenceType(t) {
74 Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) {
77 bool Reference::equals(const Value* value) const {
78 const Reference* other = valueCast<Reference>(value);
82 return referenceType == other->referenceType && privateReference == other->privateReference &&
83 id == other->id && name == other->name;
86 bool Reference::flatten(android::Res_value* outValue) const {
87 outValue->dataType = (referenceType == Reference::Type::kResource) ?
88 android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE;
89 outValue->data = util::hostToDevice32(id ? id.value().id : 0);
93 Reference* Reference::clone(StringPool* /*newPool*/) const {
94 return new Reference(*this);
97 void Reference::print(std::ostream* out) const {
98 *out << "(reference) ";
99 if (referenceType == Reference::Type::kResource) {
101 if (privateReference) {
109 *out << name.value();
112 if (id && !Res_INTERNALID(id.value().id)) {
113 *out << " " << id.value();
117 bool Id::equals(const Value* value) const {
118 return valueCast<Id>(value) != nullptr;
121 bool Id::flatten(android::Res_value* out) const {
122 out->dataType = android::Res_value::TYPE_INT_BOOLEAN;
123 out->data = util::hostToDevice32(0);
127 Id* Id::clone(StringPool* /*newPool*/) const {
128 return new Id(*this);
131 void Id::print(std::ostream* out) const {
135 String::String(const StringPool::Ref& ref) : value(ref) {
138 bool String::equals(const Value* value) const {
139 const String* other = valueCast<String>(value);
143 return *this->value == *other->value;
146 bool String::flatten(android::Res_value* outValue) const {
147 // Verify that our StringPool index is within encode-able limits.
148 if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
152 outValue->dataType = android::Res_value::TYPE_STRING;
153 outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
157 String* String::clone(StringPool* newPool) const {
158 String* str = new String(newPool->makeRef(*value));
159 str->mComment = mComment;
160 str->mSource = mSource;
164 void String::print(std::ostream* out) const {
165 *out << "(string) \"" << *value << "\"";
168 StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) {
171 bool StyledString::equals(const Value* value) const {
172 const StyledString* other = valueCast<StyledString>(value);
177 if (*this->value->str == *other->value->str) {
178 const std::vector<StringPool::Span>& spansA = this->value->spans;
179 const std::vector<StringPool::Span>& spansB = other->value->spans;
180 return std::equal(spansA.begin(), spansA.end(), spansB.begin(),
181 [](const StringPool::Span& a, const StringPool::Span& b) -> bool {
182 return *a.name == *b.name && a.firstChar == b.firstChar && a.lastChar == b.lastChar;
188 bool StyledString::flatten(android::Res_value* outValue) const {
189 if (value.getIndex() > std::numeric_limits<uint32_t>::max()) {
193 outValue->dataType = android::Res_value::TYPE_STRING;
194 outValue->data = util::hostToDevice32(static_cast<uint32_t>(value.getIndex()));
198 StyledString* StyledString::clone(StringPool* newPool) const {
199 StyledString* str = new StyledString(newPool->makeRef(value));
200 str->mComment = mComment;
201 str->mSource = mSource;
205 void StyledString::print(std::ostream* out) const {
206 *out << "(styled string) \"" << *value->str << "\"";
207 for (const StringPool::Span& span : value->spans) {
208 *out << " "<< *span.name << ":" << span.firstChar << "," << span.lastChar;
212 FileReference::FileReference(const StringPool::Ref& _path) : path(_path) {
215 bool FileReference::equals(const Value* value) const {
216 const FileReference* other = valueCast<FileReference>(value);
220 return *path == *other->path;
223 bool FileReference::flatten(android::Res_value* outValue) const {
224 if (path.getIndex() > std::numeric_limits<uint32_t>::max()) {
228 outValue->dataType = android::Res_value::TYPE_STRING;
229 outValue->data = util::hostToDevice32(static_cast<uint32_t>(path.getIndex()));
233 FileReference* FileReference::clone(StringPool* newPool) const {
234 FileReference* fr = new FileReference(newPool->makeRef(*path));
236 fr->mComment = mComment;
237 fr->mSource = mSource;
241 void FileReference::print(std::ostream* out) const {
242 *out << "(file) " << *path;
245 BinaryPrimitive::BinaryPrimitive(const android::Res_value& val) : value(val) {
248 BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) {
249 value.dataType = dataType;
253 bool BinaryPrimitive::equals(const Value* value) const {
254 const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value);
258 return this->value.dataType == other->value.dataType && this->value.data == other->value.data;
261 bool BinaryPrimitive::flatten(android::Res_value* outValue) const {
262 outValue->dataType = value.dataType;
263 outValue->data = util::hostToDevice32(value.data);
267 BinaryPrimitive* BinaryPrimitive::clone(StringPool* /*newPool*/) const {
268 return new BinaryPrimitive(*this);
271 void BinaryPrimitive::print(std::ostream* out) const {
272 switch (value.dataType) {
273 case android::Res_value::TYPE_NULL:
276 case android::Res_value::TYPE_INT_DEC:
277 *out << "(integer) " << static_cast<int32_t>(value.data);
279 case android::Res_value::TYPE_INT_HEX:
280 *out << "(integer) 0x" << std::hex << value.data << std::dec;
282 case android::Res_value::TYPE_INT_BOOLEAN:
283 *out << "(boolean) " << (value.data != 0 ? "true" : "false");
285 case android::Res_value::TYPE_INT_COLOR_ARGB8:
286 case android::Res_value::TYPE_INT_COLOR_RGB8:
287 case android::Res_value::TYPE_INT_COLOR_ARGB4:
288 case android::Res_value::TYPE_INT_COLOR_RGB4:
289 *out << "(color) #" << std::hex << value.data << std::dec;
292 *out << "(unknown 0x" << std::hex << (int) value.dataType << ") 0x"
293 << std::hex << value.data << std::dec;
298 Attribute::Attribute(bool w, uint32_t t) :
300 minInt(std::numeric_limits<int32_t>::min()),
301 maxInt(std::numeric_limits<int32_t>::max()) {
305 bool Attribute::equals(const Value* value) const {
306 const Attribute* other = valueCast<Attribute>(value);
311 return this->typeMask == other->typeMask && this->minInt == other->minInt &&
312 this->maxInt == other->maxInt &&
313 std::equal(this->symbols.begin(), this->symbols.end(),
314 other->symbols.begin(),
315 [](const Symbol& a, const Symbol& b) -> bool {
316 return a.symbol.equals(&b.symbol) && a.value == b.value;
320 Attribute* Attribute::clone(StringPool* /*newPool*/) const {
321 return new Attribute(*this);
324 void Attribute::printMask(std::ostream* out) const {
325 if (typeMask == android::ResTable_map::TYPE_ANY) {
331 if ((typeMask & android::ResTable_map::TYPE_REFERENCE) != 0) {
340 if ((typeMask & android::ResTable_map::TYPE_STRING) != 0) {
349 if ((typeMask & android::ResTable_map::TYPE_INTEGER) != 0) {
358 if ((typeMask & android::ResTable_map::TYPE_BOOLEAN) != 0) {
367 if ((typeMask & android::ResTable_map::TYPE_COLOR) != 0) {
376 if ((typeMask & android::ResTable_map::TYPE_FLOAT) != 0) {
385 if ((typeMask & android::ResTable_map::TYPE_DIMENSION) != 0) {
394 if ((typeMask & android::ResTable_map::TYPE_FRACTION) != 0) {
403 if ((typeMask & android::ResTable_map::TYPE_ENUM) != 0) {
412 if ((typeMask & android::ResTable_map::TYPE_FLAGS) != 0) {
422 void Attribute::print(std::ostream* out) const {
426 if (!symbols.empty()) {
428 << util::joiner(symbols.begin(), symbols.end(), ", ")
432 if (minInt != std::numeric_limits<int32_t>::min()) {
433 *out << " min=" << minInt;
436 if (maxInt != std::numeric_limits<int32_t>::max()) {
437 *out << " max=" << maxInt;
445 static void buildAttributeMismatchMessage(DiagMessage* msg, const Attribute* attr,
448 if (attr->typeMask & android::ResTable_map::TYPE_BOOLEAN) {
452 if (attr->typeMask & android::ResTable_map::TYPE_COLOR) {
456 if (attr->typeMask & android::ResTable_map::TYPE_DIMENSION) {
457 *msg << " dimension";
460 if (attr->typeMask & android::ResTable_map::TYPE_ENUM) {
464 if (attr->typeMask & android::ResTable_map::TYPE_FLAGS) {
468 if (attr->typeMask & android::ResTable_map::TYPE_FLOAT) {
472 if (attr->typeMask & android::ResTable_map::TYPE_FRACTION) {
476 if (attr->typeMask & android::ResTable_map::TYPE_INTEGER) {
480 if (attr->typeMask & android::ResTable_map::TYPE_REFERENCE) {
481 *msg << " reference";
484 if (attr->typeMask & android::ResTable_map::TYPE_STRING) {
488 *msg << " but got " << *value;
491 bool Attribute::matches(const Item* item, DiagMessage* outMsg) const {
492 android::Res_value val = {};
495 // Always allow references.
496 const uint32_t mask = typeMask | android::ResTable_map::TYPE_REFERENCE;
497 if (!(mask & ResourceUtils::androidTypeToAttributeTypeMask(val.dataType))) {
499 buildAttributeMismatchMessage(outMsg, this, item);
503 } else if (ResourceUtils::androidTypeToAttributeTypeMask(val.dataType) &
504 android::ResTable_map::TYPE_INTEGER) {
505 if (static_cast<int32_t>(util::deviceToHost32(val.data)) < minInt) {
507 *outMsg << *item << " is less than minimum integer " << minInt;
510 } else if (static_cast<int32_t>(util::deviceToHost32(val.data)) > maxInt) {
512 *outMsg << *item << " is greater than maximum integer " << maxInt;
520 bool Style::equals(const Value* value) const {
521 const Style* other = valueCast<Style>(value);
525 if (bool(parent) != bool(other->parent) ||
526 (parent && other->parent && !parent.value().equals(&other->parent.value()))) {
529 return std::equal(entries.begin(), entries.end(), other->entries.begin(),
530 [](const Entry& a, const Entry& b) -> bool {
531 return a.key.equals(&b.key) && a.value->equals(b.value.get());
535 Style* Style::clone(StringPool* newPool) const {
536 Style* style = new Style();
537 style->parent = parent;
538 style->parentInferred = parentInferred;
539 style->mComment = mComment;
540 style->mSource = mSource;
541 for (auto& entry : entries) {
542 style->entries.push_back(Entry{
544 std::unique_ptr<Item>(entry.value->clone(newPool))
550 void Style::print(std::ostream* out) const {
552 if (parent && parent.value().name) {
553 if (parent.value().privateReference) {
556 *out << parent.value().name.value();
559 << util::joiner(entries.begin(), entries.end(), ", ")
563 static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value) {
564 if (value.key.name) {
565 out << value.key.name.value();
570 value.value->print(&out);
574 bool Array::equals(const Value* value) const {
575 const Array* other = valueCast<Array>(value);
580 return std::equal(items.begin(), items.end(), other->items.begin(),
581 [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
582 return a->equals(b.get());
586 Array* Array::clone(StringPool* newPool) const {
587 Array* array = new Array();
588 array->mComment = mComment;
589 array->mSource = mSource;
590 for (auto& item : items) {
591 array->items.emplace_back(std::unique_ptr<Item>(item->clone(newPool)));
596 void Array::print(std::ostream* out) const {
598 << util::joiner(items.begin(), items.end(), ", ")
602 bool Plural::equals(const Value* value) const {
603 const Plural* other = valueCast<Plural>(value);
608 return std::equal(values.begin(), values.end(), other->values.begin(),
609 [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool {
610 if (bool(a) != bool(b)) {
613 return bool(a) == bool(b) || a->equals(b.get());
617 Plural* Plural::clone(StringPool* newPool) const {
618 Plural* p = new Plural();
619 p->mComment = mComment;
620 p->mSource = mSource;
621 const size_t count = values.size();
622 for (size_t i = 0; i < count; i++) {
624 p->values[i] = std::unique_ptr<Item>(values[i]->clone(newPool));
630 void Plural::print(std::ostream* out) const {
633 *out << " zero=" << *values[Zero];
637 *out << " one=" << *values[One];
641 *out << " two=" << *values[Two];
645 *out << " few=" << *values[Few];
649 *out << " many=" << *values[Many];
653 static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) {
657 bool Styleable::equals(const Value* value) const {
658 const Styleable* other = valueCast<Styleable>(value);
662 return std::equal(entries.begin(), entries.end(), other->entries.begin(),
663 [](const Reference& a, const Reference& b) -> bool {
668 Styleable* Styleable::clone(StringPool* /*newPool*/) const {
669 return new Styleable(*this);
672 void Styleable::print(std::ostream* out) const {
673 *out << "(styleable) " << " ["
674 << util::joiner(entries.begin(), entries.end(), ", ")