1fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski/*
2fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * Copyright (C) 2016 The Android Open Source Project
3fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski *
4fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * you may not use this file except in compliance with the License.
6fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * You may obtain a copy of the License at
7fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski *
8fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski *
10fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * Unless required by applicable law or agreed to in writing, software
11fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * See the License for the specific language governing permissions and
14fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * limitations under the License.
15fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski */
16fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
17d48944a745f9ed121e6bde22ef6feb3a44fbec39Adam Lesinski#include "optimize/VersionCollapser.h"
18fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
19fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski#include <algorithm>
20fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski#include <vector>
21fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
22ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski#include "ResourceTable.h"
23ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski
24fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskinamespace aapt {
25fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
26fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskitemplate <typename Iterator, typename Pred>
27fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskiclass FilterIterator {
28cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski public:
29cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  FilterIterator(Iterator begin, Iterator end, Pred pred = Pred())
30ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      : current_(begin), end_(end), pred_(pred) {
31ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    Advance();
32cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
33cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
34ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  bool HasNext() { return current_ != end_; }
35cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
36ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Iterator NextIter() {
37ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    Iterator iter = current_;
38ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    ++current_;
39ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    Advance();
40cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    return iter;
41cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
42cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
43ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  typename Iterator::reference Next() { return *NextIter(); }
44cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
45cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski private:
46ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  void Advance() {
47ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    for (; current_ != end_; ++current_) {
48ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      if (pred_(*current_)) {
49cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        return;
50cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
51fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski    }
52cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
53fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
54ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Iterator current_, end_;
55ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  Pred pred_;
56fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski};
57fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
58fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinskitemplate <typename Iterator, typename Pred>
59ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam LesinskiFilterIterator<Iterator, Pred> make_filter_iterator(Iterator begin,
60ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                    Iterator end = Iterator(),
61ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski                                                    Pred pred = Pred()) {
62cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return FilterIterator<Iterator, Pred>(begin, end, pred);
63fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski}
64fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
65fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski/**
66cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * Every Configuration with an SDK version specified that is less than minSdk
67cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * will be removed.
68cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * The exception is when there is no exact matching resource for the minSdk. The
69cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski * next smallest
70fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski * one will be kept.
71fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski */
72ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskistatic void CollapseVersions(int min_sdk, ResourceEntry* entry) {
73cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // First look for all sdks less than minSdk.
74cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto iter = entry->values.rbegin(); iter != entry->values.rend();
75cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski       ++iter) {
76cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // Check if the item was already marked for removal.
77cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    if (!(*iter)) {
78cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      continue;
79fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski    }
80fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
81cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    const ConfigDescription& config = (*iter)->config;
82ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (config.sdkVersion <= min_sdk) {
83cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // This is the first configuration we've found with a smaller or equal SDK
84cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // level
85cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // to the minimum. We MUST keep this one, but remove all others we find,
86cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // which get
87cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // overridden by this one.
88cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
89ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      ConfigDescription config_without_sdk = config.CopyWithoutSdkVersion();
90cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      auto pred = [&](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
91cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // Check that the value hasn't already been marked for removal.
92cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        if (!val) {
93cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          return false;
9487675ada47fabd1d751dc35990a95d7682b19c5bAdam Lesinski        }
9587675ada47fabd1d751dc35990a95d7682b19c5bAdam Lesinski
96cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski        // Only return Configs that differ in SDK version.
97ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        config_without_sdk.sdkVersion = val->config.sdkVersion;
98ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        return config_without_sdk == val->config &&
99ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski               val->config.sdkVersion <= min_sdk;
100cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      };
101cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
102cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Remove the rest that match.
103ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      auto filter_iter =
104ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski          make_filter_iterator(iter + 1, entry->values.rend(), pred);
105ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      while (filter_iter.HasNext()) {
106ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        filter_iter.Next() = {};
107cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
108cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    }
109cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
110cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
111cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Now erase the nullptr values.
112cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  entry->values.erase(
113cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      std::remove_if(entry->values.begin(), entry->values.end(),
114cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                     [](const std::unique_ptr<ResourceConfigValue>& val)
115cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                         -> bool { return val == nullptr; }),
116cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      entry->values.end());
117cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
118cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // Strip the version qualifiers for every resource with version <= minSdk.
119cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // This will ensure
120cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // that the resource entries are all packed together in the same ResTable_type
121cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // struct
122cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  // and take up less space in the resources.arsc table.
123cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  bool modified = false;
124ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  for (std::unique_ptr<ResourceConfigValue>& config_value : entry->values) {
125ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    if (config_value->config.sdkVersion != 0 &&
126ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        config_value->config.sdkVersion <= min_sdk) {
127cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      // Override the resource with a Configuration without an SDK.
128ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      std::unique_ptr<ResourceConfigValue> new_value =
129cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski          util::make_unique<ResourceConfigValue>(
130ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              config_value->config.CopyWithoutSdkVersion(),
131ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski              config_value->product);
132ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      new_value->value = std::move(config_value->value);
133ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski      config_value = std::move(new_value);
134cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
135cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      modified = true;
13687675ada47fabd1d751dc35990a95d7682b19c5bAdam Lesinski    }
137cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
138cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski
139cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  if (modified) {
140cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    // We've modified the keys (ConfigDescription) by changing the sdkVersion to
141ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski    // 0. We MUST re-sort to ensure ordering guarantees hold.
142cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    std::sort(entry->values.begin(), entry->values.end(),
143cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              [](const std::unique_ptr<ResourceConfigValue>& a,
144cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                 const std::unique_ptr<ResourceConfigValue>& b) -> bool {
145cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski                return a->config.compare(b->config) < 0;
146cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski              });
147cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
148fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski}
149fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
150ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinskibool VersionCollapser::Consume(IAaptContext* context, ResourceTable* table) {
151ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski  const int min_sdk = context->GetMinSdkVersion();
152cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  for (auto& package : table->packages) {
153cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski    for (auto& type : package->types) {
154cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      for (auto& entry : type->entries) {
155ce5e56e243d262a9b65459c3bd0bb9eaadd40628Adam Lesinski        CollapseVersions(min_sdk, entry.get());
156cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski      }
157fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski    }
158cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  }
159cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski  return true;
160fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski}
161fb6312fe93a8544e6a95d1c619c8cea3940cbe1aAdam Lesinski
162cacb28f2d60858106e2819cc7d95a65e8bda890bAdam Lesinski}  // namespace aapt
163