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