blob: 5ba981931f3b5699e42f975392c4cdf497935003 [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 "ConfigDescription.h"
18#include "ResourceTable.h"
19#include "SdkConstants.h"
20#include "ValueVisitor.h"
Adam Lesinski1ab598f2015-08-14 14:26:04 -070021#include "link/Linkers.h"
22
23#include <algorithm>
24#include <cassert>
25
26namespace aapt {
27
Adam Lesinskicacb28f2016-10-19 12:18:14 -070028bool shouldGenerateVersionedResource(const ResourceEntry* entry,
29 const ConfigDescription& config,
Adam Lesinski1ab598f2015-08-14 14:26:04 -070030 const int sdkVersionToGenerate) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070031 // We assume the caller is trying to generate a version greater than the
32 // current configuration.
33 assert(sdkVersionToGenerate > config.sdkVersion);
Adam Lesinskifb6312f2016-06-28 14:40:32 -070034
Adam Lesinskicacb28f2016-10-19 12:18:14 -070035 const auto endIter = entry->values.end();
36 auto iter = entry->values.begin();
37 for (; iter != endIter; ++iter) {
38 if ((*iter)->config == config) {
39 break;
Adam Lesinskie4bb9eb2016-02-12 22:18:51 -080040 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070041 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070042
Adam Lesinskicacb28f2016-10-19 12:18:14 -070043 // The source config came from this list, so it should be here.
44 assert(iter != entry->values.end());
45 ++iter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070046
Adam Lesinskicacb28f2016-10-19 12:18:14 -070047 // The next configuration either only varies in sdkVersion, or it is
48 // completely different
49 // and therefore incompatible. If it is incompatible, we must generate the
50 // versioned resource.
Adam Lesinski1ab598f2015-08-14 14:26:04 -070051
Adam Lesinskicacb28f2016-10-19 12:18:14 -070052 // NOTE: The ordering of configurations takes sdkVersion as higher precedence
53 // than other
54 // qualifiers, so we need to iterate through the entire list to be sure there
55 // are no higher sdk level versions of this resource.
56 ConfigDescription tempConfig(config);
57 for (; iter != endIter; ++iter) {
58 tempConfig.sdkVersion = (*iter)->config.sdkVersion;
59 if (tempConfig == (*iter)->config) {
60 // The two configs are the same, check the sdk version.
61 return sdkVersionToGenerate < (*iter)->config.sdkVersion;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070062 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -070063 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070064
Adam Lesinskicacb28f2016-10-19 12:18:14 -070065 // No match was found, so we should generate the versioned resource.
66 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070067}
68
69bool AutoVersioner::consume(IAaptContext* context, ResourceTable* table) {
Adam Lesinskicacb28f2016-10-19 12:18:14 -070070 for (auto& package : table->packages) {
71 for (auto& type : package->types) {
72 if (type->type != ResourceType::kStyle) {
73 continue;
74 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070075
Adam Lesinskicacb28f2016-10-19 12:18:14 -070076 for (auto& entry : type->entries) {
77 for (size_t i = 0; i < entry->values.size(); i++) {
78 ResourceConfigValue* configValue = entry->values[i].get();
79 if (configValue->config.sdkVersion >= SDK_LOLLIPOP_MR1) {
80 // If this configuration is only used on L-MR1 then we don't need
81 // to do anything since we use private attributes since that
82 // version.
83 continue;
84 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -070085
Adam Lesinskicacb28f2016-10-19 12:18:14 -070086 if (Style* style = valueCast<Style>(configValue->value.get())) {
87 Maybe<size_t> minSdkStripped;
88 std::vector<Style::Entry> stripped;
Adam Lesinski1ab598f2015-08-14 14:26:04 -070089
Adam Lesinskicacb28f2016-10-19 12:18:14 -070090 auto iter = style->entries.begin();
91 while (iter != style->entries.end()) {
92 assert(iter->key.id && "IDs must be assigned and linked");
Adam Lesinski1ab598f2015-08-14 14:26:04 -070093
Adam Lesinskicacb28f2016-10-19 12:18:14 -070094 // Find the SDK level that is higher than the configuration
95 // allows.
96 const size_t sdkLevel =
97 findAttributeSdkLevel(iter->key.id.value());
98 if (sdkLevel >
99 std::max<size_t>(configValue->config.sdkVersion, 1)) {
100 // Record that we are about to strip this.
101 stripped.emplace_back(std::move(*iter));
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700102
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700103 // We use the smallest SDK level to generate the new style.
104 if (minSdkStripped) {
105 minSdkStripped = std::min(minSdkStripped.value(), sdkLevel);
106 } else {
107 minSdkStripped = sdkLevel;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700108 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700109
110 // Erase this from this style.
111 iter = style->entries.erase(iter);
112 continue;
113 }
114 ++iter;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700115 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700116
117 if (minSdkStripped && !stripped.empty()) {
118 // We found attributes from a higher SDK level. Check that
119 // there is no other defined resource for the version we want to
120 // generate.
121 if (shouldGenerateVersionedResource(entry.get(),
122 configValue->config,
123 minSdkStripped.value())) {
124 // Let's create a new Style for this versioned resource.
125 ConfigDescription newConfig(configValue->config);
126 newConfig.sdkVersion = minSdkStripped.value();
127
128 std::unique_ptr<Style> newStyle(
129 style->clone(&table->stringPool));
130 newStyle->setComment(style->getComment());
131 newStyle->setSource(style->getSource());
132
133 // Move the previously stripped attributes into this style.
134 newStyle->entries.insert(
135 newStyle->entries.end(),
136 std::make_move_iterator(stripped.begin()),
137 std::make_move_iterator(stripped.end()));
138
139 // Insert the new Resource into the correct place.
140 entry->findOrCreateValue(newConfig, {})->value =
141 std::move(newStyle);
142 }
143 }
144 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700145 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700146 }
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700147 }
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700148 }
149 return true;
Adam Lesinski1ab598f2015-08-14 14:26:04 -0700150}
151
Adam Lesinskicacb28f2016-10-19 12:18:14 -0700152} // namespace aapt