xref: /aosp_15_r20/frameworks/base/tools/aapt2/process/ProductFilter.cpp (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
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 "process/ProductFilter.h"
18 
19 #include <algorithm>
20 
21 #include "ResourceTable.h"
22 #include "trace/TraceBuffer.h"
23 
24 namespace aapt {
25 
SelectProductToKeep(const ResourceNameRef & name,ResourceConfigValueIter begin,ResourceConfigValueIter end,android::IDiagnostics * diag)26 std::optional<ProductFilter::ResourceConfigValueIter> ProductFilter::SelectProductToKeep(
27     const ResourceNameRef& name, ResourceConfigValueIter begin, ResourceConfigValueIter end,
28     android::IDiagnostics* diag) {
29   ResourceConfigValueIter default_product_iter = end;
30   ResourceConfigValueIter selected_product_iter = end;
31 
32   for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
33     ResourceConfigValue* config_value = iter->get();
34     if (products_.find(config_value->product) != products_.end()) {
35       if (selected_product_iter != end) {
36         // We have two possible values for this product!
37         diag->Error(android::DiagMessage(config_value->value->GetSource())
38                     << "selection of product '" << config_value->product << "' for resource "
39                     << name << " is ambiguous");
40 
41         ResourceConfigValue* previously_selected_config_value = selected_product_iter->get();
42         diag->Note(android::DiagMessage(previously_selected_config_value->value->GetSource())
43                    << "product '" << previously_selected_config_value->product
44                    << "' is also a candidate");
45         return std::nullopt;
46       }
47 
48       // Select this product.
49       selected_product_iter = iter;
50     }
51 
52     if (config_value->product.empty() || config_value->product == "default") {
53       if (default_product_iter != end) {
54         // We have two possible default values.
55         diag->Error(android::DiagMessage(config_value->value->GetSource())
56                     << "multiple default products defined for resource " << name);
57 
58         ResourceConfigValue* previously_default_config_value = default_product_iter->get();
59         diag->Note(android::DiagMessage(previously_default_config_value->value->GetSource())
60                    << "default product also defined here");
61         return std::nullopt;
62       }
63 
64       // Mark the default.
65       default_product_iter = iter;
66     }
67   }
68 
69   if (remove_default_config_values_) {
70     // If we are leaving only a specific product, return early here instead of selecting the default
71     // value. Returning end here will cause this value set to be skipped, and will be removed with
72     // ClearEmptyValues method.
73     return selected_product_iter;
74   }
75 
76   if (default_product_iter == end) {
77     diag->Error(android::DiagMessage() << "no default product defined for resource " << name);
78     return std::nullopt;
79   }
80 
81   if (selected_product_iter == end) {
82     selected_product_iter = default_product_iter;
83   }
84   return selected_product_iter;
85 }
86 
Consume(IAaptContext * context,ResourceTable * table)87 bool ProductFilter::Consume(IAaptContext* context, ResourceTable* table) {
88   TRACE_NAME("ProductFilter::Consume");
89   bool error = false;
90   for (auto& pkg : table->packages) {
91     for (auto& type : pkg->types) {
92       for (auto& entry : type->entries) {
93         std::vector<std::unique_ptr<ResourceConfigValue>> new_values;
94 
95         ResourceConfigValueIter iter = entry->values.begin();
96         ResourceConfigValueIter start_range_iter = iter;
97         while (iter != entry->values.end()) {
98           ++iter;
99           if (iter == entry->values.end() || (*iter)->config != (*start_range_iter)->config) {
100             // End of the array, or we saw a different config,
101             // so this must be the end of a range of products.
102             // Select the product to keep from the set of products defined.
103             ResourceNameRef name(pkg->name, type->named_type, entry->name);
104             auto value_to_keep =
105                 SelectProductToKeep(name, start_range_iter, iter, context->GetDiagnostics());
106             if (!value_to_keep.has_value()) {
107               // An error occurred, we could not pick a product.
108               error = true;
109             } else if (auto val = value_to_keep.value(); val != iter) {
110               // We selected a product to keep. Move it to the new array.
111               if (remove_default_config_values_) {
112                 // We are filtering values with the given product. The selected value here will be
113                 // a new default value, and all other values will be removed.
114                 new_values.push_back(
115                     std::make_unique<ResourceConfigValue>((*val)->config, android::StringPiece{}));
116                 new_values.back()->value = std::move((*val)->value);
117               } else {
118                 new_values.push_back(std::move(*val));
119               }
120             }
121 
122             // Start the next range of products.
123             start_range_iter = iter;
124           }
125         }
126 
127         // Now move the new values in to place.
128         entry->values = std::move(new_values);
129       }
130     }
131   }
132 
133   if (remove_default_config_values_) {
134     ClearEmptyValues(table);
135   }
136 
137   return !error;
138 }
139 
ClearEmptyValues(ResourceTable * table)140 void ProductFilter::ClearEmptyValues(ResourceTable* table) {
141   // Clear any empty packages/types/entries, as remove_default_config_values_ may remove an entire
142   // value set.
143   CHECK(remove_default_config_values_)
144       << __func__ << " should only be called when remove_default_config_values_ is set";
145 
146   for (auto& pkg : table->packages) {
147     for (auto& type : pkg->types) {
148       std::erase_if(type->entries, [](auto& entry) { return entry->values.empty(); });
149     }
150     std::erase_if(pkg->types, [](auto& type) { return type->entries.empty(); });
151   }
152   std::erase_if(table->packages, [](auto& package) { return package->types.empty(); });
153 }
154 
155 }  // namespace aapt
156