ProductFilter.cpp revision e4bb9eb5af5b0899dc0921d5580220b20e15bd5a
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 "link/ProductFilter.h"
18
19namespace aapt {
20
21ProductFilter::ResourceConfigValueIter
22ProductFilter::selectProductToKeep(const ResourceNameRef& name,
23                                   const ResourceConfigValueIter begin,
24                                   const ResourceConfigValueIter end,
25                                   IDiagnostics* diag) {
26    ResourceConfigValueIter defaultProductIter = end;
27    ResourceConfigValueIter selectedProductIter = end;
28
29    for (ResourceConfigValueIter iter = begin; iter != end; ++iter) {
30        ResourceConfigValue* configValue = iter->get();
31        if (mProducts.find(configValue->product) != mProducts.end()) {
32            if (selectedProductIter != end) {
33                // We have two possible values for this product!
34                diag->error(DiagMessage(configValue->value->getSource())
35                            << "selection of product '" << configValue->product
36                            << "' for resource " << name << " is ambiguous");
37
38                ResourceConfigValue* previouslySelectedConfigValue = selectedProductIter->get();
39                diag->note(DiagMessage(previouslySelectedConfigValue->value->getSource())
40                           << "product '" << previouslySelectedConfigValue->product
41                           << "' is also a candidate");
42                return end;
43            }
44
45            // Select this product.
46            selectedProductIter = iter;
47        }
48
49        if (configValue->product.empty() || configValue->product == "default") {
50            if (defaultProductIter != end) {
51                // We have two possible default values.
52                diag->error(DiagMessage(configValue->value->getSource())
53                            << "multiple default products defined for resource " << name);
54
55                ResourceConfigValue* previouslyDefaultConfigValue = defaultProductIter->get();
56                diag->note(DiagMessage(previouslyDefaultConfigValue->value->getSource())
57                           << "default product also defined here");
58                return end;
59            }
60
61            // Mark the default.
62            defaultProductIter = iter;
63        }
64    }
65
66    if (defaultProductIter == end) {
67        diag->error(DiagMessage() << "no default product defined for resource " << name);
68        return end;
69    }
70
71    if (selectedProductIter == end) {
72        selectedProductIter = defaultProductIter;
73    }
74    return selectedProductIter;
75}
76
77bool ProductFilter::consume(IAaptContext* context, ResourceTable* table) {
78    bool error = false;
79    for (auto& pkg : table->packages) {
80        for (auto& type : pkg->types) {
81            for (auto& entry : type->entries) {
82                std::vector<std::unique_ptr<ResourceConfigValue>> newValues;
83
84                ResourceConfigValueIter iter = entry->values.begin();
85                ResourceConfigValueIter startRangeIter = iter;
86                while (iter != entry->values.end()) {
87                    ++iter;
88                    if (iter == entry->values.end() ||
89                            (*iter)->config != (*startRangeIter)->config) {
90
91                        // End of the array, or we saw a different config,
92                        // so this must be the end of a range of products.
93                        // Select the product to keep from the set of products defined.
94                        ResourceNameRef name(pkg->name, type->type, entry->name);
95                        auto valueToKeep = selectProductToKeep(name, startRangeIter, iter,
96                                                               context->getDiagnostics());
97                        if (valueToKeep == iter) {
98                            // An error occurred, we could not pick a product.
99                            error = true;
100                        } else {
101                            // We selected a product to keep. Move it to the new array.
102                            newValues.push_back(std::move(*valueToKeep));
103                        }
104
105                        // Start the next range of products.
106                        startRangeIter = iter;
107                    }
108                }
109
110                // Now move the new values in to place.
111                entry->values = std::move(newValues);
112            }
113        }
114    }
115    return !error;
116}
117
118} // namespace aapt
119