TableMerger.cpp revision 1ab598f46c3ff520a67f9d80194847741f3467ab
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 "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.
42        if (package->id && package->id.value() != desiredPackageId) {
43            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
184