blob: 61a1f86028c6d3728e402d00cdfeb451dc5c0493 [file] [log] [blame]
Adam Lesinskifb6312f2016-06-28 14:40:32 -07001/*
2 * Copyright (C) 2016 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 "link/Linkers.h"
19
20#include <algorithm>
21#include <vector>
22
23namespace aapt {
24
25template <typename Iterator, typename Pred>
26class FilterIterator {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070027 public:
28 FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
29 : mCurrent(begin), mEnd(end), mPred(pred) {
30 advance();
31 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070032
Adam Lesinskicacb28f2016-10-19 12:18:14 -070033 bool hasNext() { return mCurrent != mEnd; }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070034
Adam Lesinskicacb28f2016-10-19 12:18:14 -070035 Iterator nextIter() {
36 Iterator iter = mCurrent;
37 ++mCurrent;
38 advance();
39 return iter;
40 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070041
Adam Lesinskicacb28f2016-10-19 12:18:14 -070042 typename Iterator::reference next() { return *nextIter(); }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070043
Adam Lesinskicacb28f2016-10-19 12:18:14 -070044 private:
45 void advance() {
46 for (; mCurrent != mEnd; ++mCurrent) {
47 if (mPred(*mCurrent)) {
48 return;
49 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070050 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070051 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -070052
Adam Lesinskicacb28f2016-10-19 12:18:14 -070053 Iterator mCurrent, mEnd;
54 Pred mPred;
Adam Lesinskifb6312f2016-06-28 14:40:32 -070055};
56
57template <typename Iterator, typename Pred>
Adam Lesinskicacb28f2016-10-19 12:18:14 -070058FilterIterator<Iterator, Pred> makeFilterIterator(Iterator begin,
59 Iterator end = Iterator(),
60 Pred pred = Pred()) {
61 return FilterIterator<Iterator, Pred>(begin, end, pred);
Adam Lesinskifb6312f2016-06-28 14:40:32 -070062}
63
64/**
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 * Every Configuration with an SDK version specified that is less than minSdk
66 * will be removed.
67 * The exception is when there is no exact matching resource for the minSdk. The
68 * next smallest
Adam Lesinskifb6312f2016-06-28 14:40:32 -070069 * one will be kept.
70 */
71static void collapseVersions(int minSdk, ResourceEntry* entry) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070072 // First look for all sdks less than minSdk.
73 for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
74 ++iter) {
75 // Check if the item was already marked for removal.
76 if (!(*iter)) {
77 continue;
Adam Lesinskifb6312f2016-06-28 14:40:32 -070078 }
79
Adam Lesinskicacb28f2016-10-19 12:18:14 -070080 const ConfigDescription& config = (*iter)->config;
81 if (config.sdkVersion <= minSdk) {
82 // This is the first configuration we've found with a smaller or equal SDK
83 // level
84 // to the minimum. We MUST keep this one, but remove all others we find,
85 // which get
86 // overridden by this one.
Adam Lesinski87675ad2016-07-15 17:03:03 -070087
Adam Lesinskicacb28f2016-10-19 12:18:14 -070088 ConfigDescription configWithoutSdk = config;
89 configWithoutSdk.sdkVersion = 0;
90 auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
91 // Check that the value hasn't already been marked for removal.
92 if (!val) {
93 return false;
Adam Lesinski87675ad2016-07-15 17:03:03 -070094 }
Adam Lesinski87675ad2016-07-15 17:03:03 -070095
Adam Lesinskicacb28f2016-10-19 12:18:14 -070096 // Only return Configs that differ in SDK version.
97 configWithoutSdk.sdkVersion = val->config.sdkVersion;
98 return configWithoutSdk == val->config &&
99 val->config.sdkVersion <= minSdk;
100 };
101
102 // Remove the rest that match.
103 auto filterIter =
104 makeFilterIterator(iter + 1, entry->values.rend(), pred);
105 while (filterIter.hasNext()) {
106 filterIter.next() = {};
107 }
Adam Lesinski87675ad2016-07-15 17:03:03 -0700108 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700109 }
110
111 // Now erase the nullptr values.
112 entry->values.erase(
113 std::remove_if(entry->values.begin(), entry->values.end(),
114 [](const std::unique_ptr<ResourceConfigValue>& val)
115 -> bool { return val == nullptr; }),
116 entry->values.end());
117
118 // Strip the version qualifiers for every resource with version <= minSdk.
119 // This will ensure
120 // that the resource entries are all packed together in the same ResTable_type
121 // struct
122 // and take up less space in the resources.arsc table.
123 bool modified = false;
124 for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
125 if (configValue->config.sdkVersion != 0 &&
126 configValue->config.sdkVersion <= minSdk) {
127 // Override the resource with a Configuration without an SDK.
128 std::unique_ptr<ResourceConfigValue> newValue =
129 util::make_unique<ResourceConfigValue>(
130 configValue->config.copyWithoutSdkVersion(),
131 configValue->product);
132 newValue->value = std::move(configValue->value);
133 configValue = std::move(newValue);
134
135 modified = true;
136 }
137 }
138
139 if (modified) {
140 // We've modified the keys (ConfigDescription) by changing the sdkVersion to
141 // 0.
142 // We MUST re-sort to ensure ordering guarantees hold.
143 std::sort(entry->values.begin(), entry->values.end(),
144 [](const std::unique_ptr<ResourceConfigValue>& a,
145 const std::unique_ptr<ResourceConfigValue>& b) -> bool {
146 return a->config.compare(b->config) < 0;
147 });
148 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700149}
150
151bool VersionCollapser::consume(IAaptContext* context, ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700152 const int minSdk = context->getMinSdkVersion();
153 for (auto& package : table->packages) {
154 for (auto& type : package->types) {
155 for (auto& entry : type->entries) {
156 collapseVersions(minSdk, entry.get());
157 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700158 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700159 }
160 return true;
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700161}
162
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700163} // namespace aapt