Home | History | Annotate | Download | only in link
      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