Home | History | Annotate | Download | only in compile
      1 /*
      2  * Copyright (C) 2015 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 "compile/IdAssigner.h"
     18 
     19 #include <map>
     20 
     21 #include "android-base/logging.h"
     22 
     23 #include "ResourceTable.h"
     24 #include "process/IResourceTableConsumer.h"
     25 #include "util/Util.h"
     26 
     27 namespace aapt {
     28 
     29 /**
     30  * Assigns the intended ID to the ResourceTablePackage, ResourceTableType, and
     31  * ResourceEntry,
     32  * as long as there is no existing ID or the ID is the same.
     33  */
     34 static bool AssignId(IDiagnostics* diag, const ResourceId& id,
     35                      const ResourceName& name, ResourceTablePackage* pkg,
     36                      ResourceTableType* type, ResourceEntry* entry) {
     37   if (pkg->id.value() == id.package_id()) {
     38     if (!type->id || type->id.value() == id.type_id()) {
     39       type->id = id.type_id();
     40 
     41       if (!entry->id || entry->id.value() == id.entry_id()) {
     42         entry->id = id.entry_id();
     43         return true;
     44       }
     45     }
     46   }
     47 
     48   const ResourceId existing_id(pkg->id.value(), type->id ? type->id.value() : 0,
     49                                entry->id ? entry->id.value() : 0);
     50   diag->Error(DiagMessage() << "can't assign ID " << id << " to resource "
     51                             << name << " with conflicting ID " << existing_id);
     52   return false;
     53 }
     54 
     55 bool IdAssigner::Consume(IAaptContext* context, ResourceTable* table) {
     56   std::map<ResourceId, ResourceName> assigned_ids;
     57 
     58   for (auto& package : table->packages) {
     59     CHECK(bool(package->id)) << "packages must have manually assigned IDs";
     60 
     61     for (auto& type : package->types) {
     62       for (auto& entry : type->entries) {
     63         const ResourceName name(package->name, type->type, entry->name);
     64 
     65         if (assigned_id_map_) {
     66           // Assign the pre-assigned stable ID meant for this resource.
     67           const auto iter = assigned_id_map_->find(name);
     68           if (iter != assigned_id_map_->end()) {
     69             const ResourceId assigned_id = iter->second;
     70             const bool result =
     71                 AssignId(context->GetDiagnostics(), assigned_id, name,
     72                          package.get(), type.get(), entry.get());
     73             if (!result) {
     74               return false;
     75             }
     76           }
     77         }
     78 
     79         if (package->id && type->id && entry->id) {
     80           // If the ID is set for this resource, then reserve it.
     81           ResourceId resource_id(package->id.value(), type->id.value(),
     82                                  entry->id.value());
     83           auto result = assigned_ids.insert({resource_id, name});
     84           const ResourceName& existing_name = result.first->second;
     85           if (!result.second) {
     86             context->GetDiagnostics()->Error(
     87                 DiagMessage() << "resource " << name << " has same ID "
     88                               << resource_id << " as " << existing_name);
     89             return false;
     90           }
     91         }
     92       }
     93     }
     94   }
     95 
     96   if (assigned_id_map_) {
     97     // Reserve all the IDs mentioned in the stable ID map. That way we won't
     98     // assign
     99     // IDs that were listed in the map if they don't exist in the table.
    100     for (const auto& stable_id_entry : *assigned_id_map_) {
    101       const ResourceName& pre_assigned_name = stable_id_entry.first;
    102       const ResourceId& pre_assigned_id = stable_id_entry.second;
    103       auto result = assigned_ids.insert({pre_assigned_id, pre_assigned_name});
    104       const ResourceName& existing_name = result.first->second;
    105       if (!result.second && existing_name != pre_assigned_name) {
    106         context->GetDiagnostics()->Error(
    107             DiagMessage() << "stable ID " << pre_assigned_id << " for resource "
    108                           << pre_assigned_name
    109                           << " is already taken by resource " << existing_name);
    110         return false;
    111       }
    112     }
    113   }
    114 
    115   // Assign any resources without IDs the next available ID. Gaps will be filled
    116   // if possible,
    117   // unless those IDs have been reserved.
    118 
    119   const auto assigned_ids_iter_end = assigned_ids.end();
    120   for (auto& package : table->packages) {
    121     CHECK(bool(package->id)) << "packages must have manually assigned IDs";
    122 
    123     // Build a half filled ResourceId object, which will be used to find the
    124     // closest matching
    125     // reserved ID in the assignedId map. From that point the next available
    126     // type ID can be
    127     // found.
    128     ResourceId resource_id(package->id.value(), 0, 0);
    129     uint8_t next_expected_type_id = 1;
    130 
    131     // Find the closest matching ResourceId that is <= the one with only the
    132     // package set.
    133     auto next_type_iter = assigned_ids.lower_bound(resource_id);
    134     for (auto& type : package->types) {
    135       if (!type->id) {
    136         // We need to assign a type ID. Iterate over the reserved IDs until we
    137         // find
    138         // some type ID that is a distance of 2 greater than the last one we've
    139         // seen.
    140         // That means there is an available type ID between these reserved IDs.
    141         while (next_type_iter != assigned_ids_iter_end) {
    142           if (next_type_iter->first.package_id() != package->id.value()) {
    143             break;
    144           }
    145 
    146           const uint8_t type_id = next_type_iter->first.type_id();
    147           if (type_id > next_expected_type_id) {
    148             // There is a gap in the type IDs, so use the missing one.
    149             type->id = next_expected_type_id++;
    150             break;
    151           }
    152 
    153           // Set our expectation to be the next type ID after the reserved one
    154           // we
    155           // just saw.
    156           next_expected_type_id = type_id + 1;
    157 
    158           // Move to the next reserved ID.
    159           ++next_type_iter;
    160         }
    161 
    162         if (!type->id) {
    163           // We must have hit the end of the reserved IDs and not found a gap.
    164           // That means the next ID is available.
    165           type->id = next_expected_type_id++;
    166         }
    167       }
    168 
    169       resource_id = ResourceId(package->id.value(), type->id.value(), 0);
    170       uint16_t next_expected_entry_id = 0;
    171 
    172       // Find the closest matching ResourceId that is <= the one with only the
    173       // package
    174       // and type set.
    175       auto next_entry_iter = assigned_ids.lower_bound(resource_id);
    176       for (auto& entry : type->entries) {
    177         if (!entry->id) {
    178           // We need to assign an entry ID. Iterate over the reserved IDs until
    179           // we find
    180           // some entry ID that is a distance of 2 greater than the last one
    181           // we've seen.
    182           // That means there is an available entry ID between these reserved
    183           // IDs.
    184           while (next_entry_iter != assigned_ids_iter_end) {
    185             if (next_entry_iter->first.package_id() != package->id.value() ||
    186                 next_entry_iter->first.type_id() != type->id.value()) {
    187               break;
    188             }
    189 
    190             const uint16_t entry_id = next_entry_iter->first.entry_id();
    191             if (entry_id > next_expected_entry_id) {
    192               // There is a gap in the entry IDs, so use the missing one.
    193               entry->id = next_expected_entry_id++;
    194               break;
    195             }
    196 
    197             // Set our expectation to be the next type ID after the reserved one
    198             // we
    199             // just saw.
    200             next_expected_entry_id = entry_id + 1;
    201 
    202             // Move to the next reserved entry ID.
    203             ++next_entry_iter;
    204           }
    205 
    206           if (!entry->id) {
    207             // We must have hit the end of the reserved IDs and not found a gap.
    208             // That means the next ID is available.
    209             entry->id = next_expected_entry_id++;
    210           }
    211         }
    212       }
    213     }
    214   }
    215   return true;
    216 }
    217 
    218 }  // namespace aapt
    219