ResourceTable.cpp revision 6ff19664f9279023c96e5a65c3059e1ef4beac0f
1/* 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 "ConfigDescription.h" 18#include "Logger.h" 19#include "NameMangler.h" 20#include "ResourceTable.h" 21#include "ResourceValues.h" 22#include "Util.h" 23 24#include <algorithm> 25#include <androidfw/ResourceTypes.h> 26#include <memory> 27#include <string> 28#include <tuple> 29 30namespace aapt { 31 32static bool compareConfigs(const ResourceConfigValue& lhs, const ConfigDescription& rhs) { 33 return lhs.config < rhs; 34} 35 36static bool lessThanType(const std::unique_ptr<ResourceTableType>& lhs, ResourceType rhs) { 37 return lhs->type < rhs; 38} 39 40static bool lessThanEntry(const std::unique_ptr<ResourceEntry>& lhs, const StringPiece16& rhs) { 41 return lhs->name.compare(0, lhs->name.size(), rhs.data(), rhs.size()) < 0; 42} 43 44ResourceTable::ResourceTable() : mPackageId(kUnsetPackageId) { 45} 46 47std::unique_ptr<ResourceTableType>& ResourceTable::findOrCreateType(ResourceType type) { 48 auto last = mTypes.end(); 49 auto iter = std::lower_bound(mTypes.begin(), last, type, lessThanType); 50 if (iter != last) { 51 if ((*iter)->type == type) { 52 return *iter; 53 } 54 } 55 return *mTypes.emplace(iter, new ResourceTableType{ type }); 56} 57 58std::unique_ptr<ResourceEntry>& ResourceTable::findOrCreateEntry( 59 std::unique_ptr<ResourceTableType>& type, const StringPiece16& name) { 60 auto last = type->entries.end(); 61 auto iter = std::lower_bound(type->entries.begin(), last, name, lessThanEntry); 62 if (iter != last) { 63 if (name == (*iter)->name) { 64 return *iter; 65 } 66 } 67 return *type->entries.emplace(iter, new ResourceEntry{ name }); 68} 69 70struct IsAttributeVisitor : ConstValueVisitor { 71 bool isAttribute = false; 72 73 void visit(const Attribute&, ValueVisitorArgs&) override { 74 isAttribute = true; 75 } 76 77 operator bool() { 78 return isAttribute; 79 } 80}; 81 82/** 83 * The default handler for collisions. A return value of -1 means keep the 84 * existing value, 0 means fail, and +1 means take the incoming value. 85 */ 86static int defaultCollisionHandler(const Value& existing, const Value& incoming) { 87 IsAttributeVisitor existingIsAttr, incomingIsAttr; 88 existing.accept(existingIsAttr, {}); 89 incoming.accept(incomingIsAttr, {}); 90 91 if (!incomingIsAttr) { 92 if (incoming.isWeak()) { 93 // We're trying to add a weak resource but a resource 94 // already exists. Keep the existing. 95 return -1; 96 } else if (existing.isWeak()) { 97 // Override the weak resource with the new strong resource. 98 return 1; 99 } 100 // The existing and incoming values are strong, this is an error 101 // if the values are not both attributes. 102 return 0; 103 } 104 105 if (!existingIsAttr) { 106 if (existing.isWeak()) { 107 // The existing value is not an attribute and it is weak, 108 // so take the incoming attribute value. 109 return 1; 110 } 111 // The existing value is not an attribute and it is strong, 112 // so the incoming attribute value is an error. 113 return 0; 114 } 115 116 // 117 // Attribute specific handling. At this point we know both 118 // values are attributes. Since we can declare and define 119 // attributes all-over, we do special handling to see 120 // which definition sticks. 121 // 122 const Attribute& existingAttr = static_cast<const Attribute&>(existing); 123 const Attribute& incomingAttr = static_cast<const Attribute&>(incoming); 124 if (existingAttr.typeMask == incomingAttr.typeMask) { 125 // The two attributes are both DECLs, but they are plain attributes 126 // with the same formats. 127 // Keep the strongest one. 128 return existingAttr.isWeak() ? 1 : -1; 129 } 130 131 if (existingAttr.isWeak() && existingAttr.typeMask == android::ResTable_map::TYPE_ANY) { 132 // Any incoming attribute is better than this. 133 return 1; 134 } 135 136 if (incomingAttr.isWeak() && incomingAttr.typeMask == android::ResTable_map::TYPE_ANY) { 137 // The incoming attribute may be a USE instead of a DECL. 138 // Keep the existing attribute. 139 return -1; 140 } 141 return 0; 142} 143 144static constexpr const char16_t* kValidNameChars = u"._-"; 145 146bool ResourceTable::addResource(const ResourceNameRef& name, const ResourceId resId, 147 const ConfigDescription& config, const SourceLine& source, 148 std::unique_ptr<Value> value) { 149 if (!name.package.empty() && name.package != mPackage) { 150 Logger::error(source) 151 << "resource '" 152 << name 153 << "' has incompatible package. Must be '" 154 << mPackage 155 << "'." 156 << std::endl; 157 return false; 158 } 159 160 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars); 161 if (badCharIter != name.entry.end()) { 162 Logger::error(source) 163 << "resource '" 164 << name 165 << "' has invalid entry name '" 166 << name.entry 167 << "'. Invalid character '" 168 << StringPiece16(badCharIter, 1) 169 << "'." 170 << std::endl; 171 return false; 172 } 173 174 std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); 175 if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && 176 type->typeId != resId.typeId()) { 177 Logger::error(source) 178 << "trying to add resource '" 179 << name 180 << "' with ID " 181 << resId 182 << " but type '" 183 << type->type 184 << "' already has ID " 185 << std::hex << type->typeId << std::dec 186 << "." 187 << std::endl; 188 return false; 189 } 190 191 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); 192 if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && 193 entry->entryId != resId.entryId()) { 194 Logger::error(source) 195 << "trying to add resource '" 196 << name 197 << "' with ID " 198 << resId 199 << " but resource already has ID " 200 << ResourceId(mPackageId, type->typeId, entry->entryId) 201 << "." 202 << std::endl; 203 return false; 204 } 205 206 const auto endIter = std::end(entry->values); 207 auto iter = std::lower_bound(std::begin(entry->values), endIter, config, compareConfigs); 208 if (iter == endIter || iter->config != config) { 209 // This resource did not exist before, add it. 210 entry->values.insert(iter, ResourceConfigValue{ config, source, {}, std::move(value) }); 211 } else { 212 int collisionResult = defaultCollisionHandler(*iter->value, *value); 213 if (collisionResult > 0) { 214 // Take the incoming value. 215 *iter = ResourceConfigValue{ config, source, {}, std::move(value) }; 216 } else if (collisionResult == 0) { 217 Logger::error(source) 218 << "duplicate value for resource '" << name << "' " 219 << "with config '" << iter->config << "'." 220 << std::endl; 221 222 Logger::error(iter->source) 223 << "resource previously defined here." 224 << std::endl; 225 return false; 226 } 227 } 228 229 if (resId.isValid()) { 230 type->typeId = resId.typeId(); 231 entry->entryId = resId.entryId(); 232 } 233 return true; 234} 235 236bool ResourceTable::addResource(const ResourceNameRef& name, const ConfigDescription& config, 237 const SourceLine& source, std::unique_ptr<Value> value) { 238 return addResource(name, ResourceId{}, config, source, std::move(value)); 239} 240 241bool ResourceTable::markPublic(const ResourceNameRef& name, const ResourceId resId, 242 const SourceLine& source) { 243 if (!name.package.empty() && name.package != mPackage) { 244 Logger::error(source) 245 << "resource '" 246 << name 247 << "' has incompatible package. Must be '" 248 << mPackage 249 << "'." 250 << std::endl; 251 return false; 252 } 253 254 auto badCharIter = util::findNonAlphaNumericAndNotInSet(name.entry, kValidNameChars); 255 if (badCharIter != name.entry.end()) { 256 Logger::error(source) 257 << "resource '" 258 << name 259 << "' has invalid entry name '" 260 << name.entry 261 << "'. Invalid character '" 262 << StringPiece16(badCharIter, 1) 263 << "'." 264 << std::endl; 265 return false; 266 } 267 268 std::unique_ptr<ResourceTableType>& type = findOrCreateType(name.type); 269 if (resId.isValid() && type->typeId != ResourceTableType::kUnsetTypeId && 270 type->typeId != resId.typeId()) { 271 Logger::error(source) 272 << "trying to make resource '" 273 << name 274 << "' public with ID " 275 << resId 276 << " but type '" 277 << type->type 278 << "' already has ID " 279 << std::hex << type->typeId << std::dec 280 << "." 281 << std::endl; 282 return false; 283 } 284 285 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, name.entry); 286 if (resId.isValid() && entry->entryId != ResourceEntry::kUnsetEntryId && 287 entry->entryId != resId.entryId()) { 288 Logger::error(source) 289 << "trying to make resource '" 290 << name 291 << "' public with ID " 292 << resId 293 << " but resource already has ID " 294 << ResourceId(mPackageId, type->typeId, entry->entryId) 295 << "." 296 << std::endl; 297 return false; 298 } 299 300 type->publicStatus.isPublic = true; 301 entry->publicStatus.isPublic = true; 302 entry->publicStatus.source = source; 303 304 if (resId.isValid()) { 305 type->typeId = resId.typeId(); 306 entry->entryId = resId.entryId(); 307 } 308 return true; 309} 310 311bool ResourceTable::merge(ResourceTable&& other) { 312 const bool mangleNames = mPackage != other.getPackage(); 313 std::u16string mangledName; 314 315 for (auto& otherType : other) { 316 std::unique_ptr<ResourceTableType>& type = findOrCreateType(otherType->type); 317 if (otherType->publicStatus.isPublic) { 318 if (type->publicStatus.isPublic && type->typeId != otherType->typeId) { 319 Logger::error() << "can not merge type '" << type->type 320 << "': conflicting public IDs " 321 << "(" << type->typeId << " vs " << otherType->typeId << ")." 322 << std::endl; 323 return false; 324 } 325 type->publicStatus = std::move(otherType->publicStatus); 326 type->typeId = otherType->typeId; 327 } 328 329 for (auto& otherEntry : otherType->entries) { 330 const std::u16string* nameToAdd = &otherEntry->name; 331 if (mangleNames) { 332 mangledName = otherEntry->name; 333 NameMangler::mangle(other.getPackage(), &mangledName); 334 nameToAdd = &mangledName; 335 } 336 337 std::unique_ptr<ResourceEntry>& entry = findOrCreateEntry(type, *nameToAdd); 338 if (otherEntry->publicStatus.isPublic) { 339 if (entry->publicStatus.isPublic && entry->entryId != otherEntry->entryId) { 340 Logger::error() << "can not merge entry '" << type->type << "/" << entry->name 341 << "': conflicting public IDs " 342 << "(" << entry->entryId << " vs " << entry->entryId << ")." 343 << std::endl; 344 return false; 345 } 346 entry->publicStatus = std::move(otherEntry->publicStatus); 347 entry->entryId = otherEntry->entryId; 348 } 349 350 for (ResourceConfigValue& otherValue : otherEntry->values) { 351 auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), 352 otherValue.config, compareConfigs); 353 if (iter != entry->values.end() && iter->config == otherValue.config) { 354 int collisionResult = defaultCollisionHandler(*iter->value, *otherValue.value); 355 if (collisionResult > 0) { 356 // Take the incoming value. 357 iter->source = std::move(otherValue.source); 358 iter->comment = std::move(otherValue.comment); 359 iter->value = std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)); 360 } else if (collisionResult == 0) { 361 ResourceNameRef resourceName = { mPackage, type->type, entry->name }; 362 Logger::error(otherValue.source) 363 << "resource '" << resourceName << "' has a conflicting value for " 364 << "configuration (" << otherValue.config << ")." 365 << std::endl; 366 Logger::note(iter->source) << "originally defined here." << std::endl; 367 return false; 368 } 369 } else { 370 entry->values.insert(iter, ResourceConfigValue{ 371 otherValue.config, 372 std::move(otherValue.source), 373 std::move(otherValue.comment), 374 std::unique_ptr<Value>(otherValue.value->clone(&mValuePool)), 375 }); 376 } 377 } 378 } 379 } 380 return true; 381} 382 383std::tuple<const ResourceTableType*, const ResourceEntry*> 384ResourceTable::findResource(const ResourceNameRef& name) const { 385 if (name.package != mPackage) { 386 return {}; 387 } 388 389 auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType); 390 if (iter == mTypes.end() || (*iter)->type != name.type) { 391 return {}; 392 } 393 394 const std::unique_ptr<ResourceTableType>& type = *iter; 395 auto iter2 = std::lower_bound(type->entries.begin(), type->entries.end(), name.entry, 396 lessThanEntry); 397 if (iter2 == type->entries.end() || name.entry != (*iter2)->name) { 398 return {}; 399 } 400 return std::make_tuple(iter->get(), iter2->get()); 401} 402 403} // namespace aapt 404