blob: 794090d0b74c8c096aca9ff53a4cb7a2d50b964f [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 "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 '"
Adam Lesinskica2fc352015-04-03 12:08:26 -0700167 << StringPiece16(badCharIter, 1)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800168 << "'."
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 '"
Adam Lesinskica2fc352015-04-03 12:08:26 -0700261 << StringPiece16(badCharIter, 1)
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800262 << "'."
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) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700317 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800318 }
319
320 auto iter = std::lower_bound(mTypes.begin(), mTypes.end(), name.type, lessThanType);
321 if (iter == mTypes.end() || (*iter)->type != name.type) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700322 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800323 }
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) {
Adam Lesinskica2fc352015-04-03 12:08:26 -0700329 return {};
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800330 }
Adam Lesinskica2fc352015-04-03 12:08:26 -0700331 return std::make_tuple(iter->get(), iter2->get());
Adam Lesinski6f6ceb72014-11-14 14:48:12 -0800332}
333
334} // namespace aapt