blob: 0d63b976936ecd49147bdc9fc9ff297771012eea [file] [log] [blame]
Adam Lesinski1ab598f2015-08-14 14:26:04 -07001/*
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 "ResourceTable.h"
18#include "ResourceValues.h"
19#include "ValueVisitor.h"
20
21#include "link/TableMerger.h"
22#include "util/Util.h"
23
24#include <cassert>
25
26namespace aapt {
27
28TableMerger::TableMerger(IAaptContext* context, ResourceTable* outTable) :
29 mContext(context), mMasterTable(outTable) {
30 // Create the desired package that all tables will be merged into.
31 mMasterPackage = mMasterTable->createPackage(
32 mContext->getCompilationPackage(), mContext->getPackageId());
33 assert(mMasterPackage && "package name or ID already taken");
34}
35
36bool TableMerger::merge(const Source& src, ResourceTable* table) {
37 const uint8_t desiredPackageId = mContext->getPackageId();
38
39 bool error = false;
40 for (auto& package : table->packages) {
41 // Warn of packages with an unrelated ID.
Adam Lesinski9ba47d82015-10-13 11:37:10 -070042 if (package->id && package->id.value() != 0x0 && package->id.value() != desiredPackageId) {
Adam Lesinski1ab598f2015-08-14 14:26:04 -070043 mContext->getDiagnostics()->warn(DiagMessage(src)
44 << "ignoring package " << package->name);
45 continue;
46 }
47
48 bool manglePackage = false;
49 if (!package->name.empty() && mContext->getCompilationPackage() != package->name) {
50 manglePackage = true;
51 mMergedPackages.insert(package->name);
52 }
53
54 // Merge here. Once the entries are merged and mangled, any references to
55 // them are still valid. This is because un-mangled references are
56 // mangled, then looked up at resolution time.
57 // Also, when linking, we convert references with no package name to use
58 // the compilation package name.
59 if (!doMerge(src, table, package.get(), manglePackage)) {
60 error = true;
61 }
62 }
63 return !error;
64}
65
66bool TableMerger::doMerge(const Source& src, ResourceTable* srcTable,
67 ResourceTablePackage* srcPackage, const bool manglePackage) {
68 bool error = false;
69
70 for (auto& srcType : srcPackage->types) {
71 ResourceTableType* dstType = mMasterPackage->findOrCreateType(srcType->type);
72 if (srcType->publicStatus.isPublic) {
73 if (dstType->publicStatus.isPublic && dstType->id && srcType->id
74 && dstType->id.value() == srcType->id.value()) {
75 // Both types are public and have different IDs.
76 mContext->getDiagnostics()->error(DiagMessage(src)
77 << "can not merge type '"
78 << srcType->type
79 << "': conflicting public IDs");
80 error = true;
81 continue;
82 }
83
84 dstType->publicStatus = std::move(srcType->publicStatus);
85 dstType->id = srcType->id;
86 }
87
88 for (auto& srcEntry : srcType->entries) {
89 ResourceEntry* dstEntry;
90 if (manglePackage) {
91 dstEntry = dstType->findOrCreateEntry(NameMangler::mangleEntry(
92 srcPackage->name, srcEntry->name));
93 } else {
94 dstEntry = dstType->findOrCreateEntry(srcEntry->name);
95 }
96
97 if (srcEntry->publicStatus.isPublic) {
98 if (dstEntry->publicStatus.isPublic && dstEntry->id && srcEntry->id
99 && dstEntry->id.value() != srcEntry->id.value()) {
100 // Both entries are public and have different IDs.
101 mContext->getDiagnostics()->error(DiagMessage(src)
102 << "can not merge entry '"
103 << srcEntry->name
104 << "': conflicting public IDs");
105 error = true;
106 continue;
107 }
108
109 dstEntry->publicStatus = std::move(srcEntry->publicStatus);
110 dstEntry->id = srcEntry->id;
111 }
112
113 for (ResourceConfigValue& srcValue : srcEntry->values) {
114 auto cmp = [](const ResourceConfigValue& a,
115 const ConfigDescription& b) -> bool {
116 return a.config < b;
117 };
118
119 auto iter = std::lower_bound(dstEntry->values.begin(), dstEntry->values.end(),
120 srcValue.config, cmp);
121
122 if (iter != dstEntry->values.end() && iter->config == srcValue.config) {
123 const int collisionResult = ResourceTable::resolveValueCollision(
124 iter->value.get(), srcValue.value.get());
125 if (collisionResult == 0) {
126 // Error!
127 ResourceNameRef resourceName =
128 { srcPackage->name, srcType->type, srcEntry->name };
129 mContext->getDiagnostics()->error(DiagMessage(srcValue.source)
130 << "resource '" << resourceName
131 << "' has a conflicting value for "
132 << "configuration ("
133 << srcValue.config << ")");
134 mContext->getDiagnostics()->note(DiagMessage(iter->source)
135 << "originally defined here");
136 error = true;
137 continue;
138 } else if (collisionResult < 0) {
139 // Keep our existing value.
140 continue;
141 }
142
143 } else {
144 // Insert a new value.
145 iter = dstEntry->values.insert(iter,
146 ResourceConfigValue{ srcValue.config });
147 }
148
149 iter->source = std::move(srcValue.source);
150 iter->comment = std::move(srcValue.comment);
151 if (manglePackage) {
152 iter->value = cloneAndMangle(srcTable, srcPackage->name,
153 srcValue.value.get());
154 } else {
155 iter->value = clone(srcValue.value.get());
156 }
157 }
158 }
159 }
160 return !error;
161}
162
163std::unique_ptr<Value> TableMerger::cloneAndMangle(ResourceTable* table,
164 const std::u16string& package,
165 Value* value) {
166 if (FileReference* f = valueCast<FileReference>(value)) {
167 // Mangle the path.
168 StringPiece16 prefix, entry, suffix;
169 if (util::extractResFilePathParts(*f->path, &prefix, &entry, &suffix)) {
170 std::u16string mangledEntry = NameMangler::mangleEntry(package, entry.toString());
171 std::u16string newPath = prefix.toString() + mangledEntry + suffix.toString();
172 mFilesToMerge.push(FileToMerge{ table, *f->path, newPath });
173 return util::make_unique<FileReference>(mMasterTable->stringPool.makeRef(newPath));
174 }
175 }
176 return clone(value);
177}
178
179std::unique_ptr<Value> TableMerger::clone(Value* value) {
180 return std::unique_ptr<Value>(value->clone(&mMasterTable->stringPool));
181}
182
183} // namespace aapt