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 19 namespace aapt { 20 21 ProductFilter::ResourceConfigValueIter 22 ProductFilter::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 77 bool 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