blob: 949d656f44a099899c74b08da864af0a5568efdd [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 {
27public:
28 FilterIterator(Iterator begin, Iterator end, Pred pred=Pred()) :
29 mCurrent(begin), mEnd(end), mPred(pred) {
30 advance();
31 }
32
33 bool hasNext() {
34 return mCurrent != mEnd;
35 }
36
37 Iterator nextIter() {
38 Iterator iter = mCurrent;
39 ++mCurrent;
40 advance();
41 return iter;
42 }
43
44 typename Iterator::reference next() {
45 return *nextIter();
46 }
47
48private:
49 void advance() {
50 for (; mCurrent != mEnd; ++mCurrent) {
51 if (mPred(*mCurrent)) {
52 return;
53 }
54 }
55 }
56
57 Iterator mCurrent, mEnd;
58 Pred mPred;
59};
60
61template <typename Iterator, typename Pred>
62FilterIterator<Iterator, Pred> makeFilterIterator(Iterator begin, Iterator end=Iterator(),
63 Pred pred=Pred()) {
64 return FilterIterator<Iterator, Pred>(begin, end, pred);
65}
66
67/**
68 * Every Configuration with an SDK version specified that is less than minSdk will be removed.
69 * The exception is when there is no exact matching resource for the minSdk. The next smallest
70 * one will be kept.
71 */
72static void collapseVersions(int minSdk, ResourceEntry* entry) {
73 // First look for all sdks less than minSdk.
74 for (auto iter = entry->values.rbegin(); iter != entry->values.rend(); ++iter) {
75 // Check if the item was already marked for removal.
76 if (!(*iter)) {
77 continue;
78 }
79
80 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 level
83 // to the minimum. We MUST keep this one, but remove all others we find, which get
84 // overridden by this one.
85
86 ConfigDescription configWithoutSdk = config;
87 configWithoutSdk.sdkVersion = 0;
88 auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
89 // Check that the value hasn't already been marked for removal.
90 if (!val) {
91 return false;
92 }
93
94 // Only return Configs that differ in SDK version.
95 configWithoutSdk.sdkVersion = val->config.sdkVersion;
96 return configWithoutSdk == val->config && val->config.sdkVersion <= minSdk;
97 };
98
99 // Remove the rest that match.
100 auto filterIter = makeFilterIterator(iter + 1, entry->values.rend(), pred);
101 while (filterIter.hasNext()) {
102 filterIter.next() = {};
103 }
104 }
105 }
106
107 // Now erase the nullptr values.
108 entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
109 [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
110 return val == nullptr;
111 }), entry->values.end());
Adam Lesinski87675ad2016-07-15 17:03:03 -0700112
113 // Strip the version qualifiers for every resource with version <= minSdk. This will ensure
114 // that the resource entries are all packed together in the same ResTable_type struct
115 // and take up less space in the resources.arsc table.
116 bool modified = false;
117 for (std::unique_ptr<ResourceConfigValue>& configValue : entry->values) {
118 if (configValue->config.sdkVersion != 0 && configValue->config.sdkVersion <= minSdk) {
119 // Override the resource with a Configuration without an SDK.
120 std::unique_ptr<ResourceConfigValue> newValue = util::make_unique<ResourceConfigValue>(
121 configValue->config.copyWithoutSdkVersion(), configValue->product);
122 newValue->value = std::move(configValue->value);
123 configValue = std::move(newValue);
124
125 modified = true;
126 }
127 }
128
129 if (modified) {
130 // We've modified the keys (ConfigDescription) by changing the sdkVersion to 0.
131 // We MUST re-sort to ensure ordering guarantees hold.
132 std::sort(entry->values.begin(), entry->values.end(),
133 [](const std::unique_ptr<ResourceConfigValue>& a,
134 const std::unique_ptr<ResourceConfigValue>& b) -> bool {
135 return a->config.compare(b->config) < 0;
136 });
137 }
Adam Lesinskifb6312f2016-06-28 14:40:32 -0700138}
139
140bool VersionCollapser::consume(IAaptContext* context, ResourceTable* table) {
141 const int minSdk = context->getMinSdkVersion();
142 for (auto& package : table->packages) {
143 for (auto& type : package->types) {
144 for (auto& entry : type->entries) {
145 collapseVersions(minSdk, entry.get());
146 }
147 }
148 }
149 return true;
150}
151
152} // namespace aapt