text: /*
   * Copyright (C) 2016 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "ResourceTable.h"
  #include "ResourceUtils.h"
  #include "ValueVisitor.h"
  #include "proto/ProtoHelpers.h"
  #include "proto/ProtoSerialize.h"
  
  #include <androidfw/ResourceTypes.h>
  
  namespace aapt {
  
  namespace {
  
  class ReferenceIdToNameVisitor : public ValueVisitor {
  public:
      using ValueVisitor::visit;
  
      ReferenceIdToNameVisitor(const std::map<ResourceId, ResourceNameRef>* mapping) :
              mMapping(mapping) {
          assert(mMapping);
      }
  
      void visit(Reference* reference) override {
          if (!reference->id || !reference->id.value().isValid()) {
              return;
          }
  
          ResourceId id = reference->id.value();
          auto cacheIter = mMapping->find(id);
          if (cacheIter != mMapping->end()) {
              reference->name = cacheIter->second.toResourceName();
          }
      }
  
  private:
      const std::map<ResourceId, ResourceNameRef>* mMapping;
  };
  
  class PackagePbDeserializer {
  public:
      PackagePbDeserializer(const android::ResStringPool* valuePool,
                            const android::ResStringPool* sourcePool,
                            const android::ResStringPool* symbolPool,
                            const Source& source, IDiagnostics* diag) :
              mValuePool(valuePool), mSourcePool(sourcePool), mSymbolPool(symbolPool),
              mSource(source), mDiag(diag) {
      }
  
  public:
      bool deserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
          Maybe<uint8_t> id;
          if (pbPackage.has_package_id()) {
              id = static_cast<uint8_t>(pbPackage.package_id());
          }
  
          std::map<ResourceId, ResourceNameRef> idIndex;
  
          ResourceTablePackage* pkg = table->createPackage(
                  util::utf8ToUtf16(pbPackage.package_name()), id);
          for (const pb::Type& pbType : pbPackage.types()) {
              const ResourceType* resType = parseResourceType(util::utf8ToUtf16(pbType.name()));
              if (!resType) {
                  mDiag->error(DiagMessage(mSource) << "unknown type '" << pbType.name() << "'");
                  return {};
              }
  
              ResourceTableType* type = pkg->findOrCreateType(*resType);
  
              for (const pb::Entry& pbEntry : pbType.entries()) {
                  ResourceEntry* entry = type->findOrCreateEntry(util::utf8ToUtf16(pbEntry.name()));
  
                  // Deserialize the symbol status (public/private with source and comments).
                  if (pbEntry.has_symbol_status()) {
                      const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
                      if (pbStatus.has_source()) {
                          deserializeSourceFromPb(pbStatus.source(), *mSourcePool,
                                                  &entry->symbolStatus.source);
                      }
  
                      if (pbStatus.has_comment()) {
                          entry->symbolStatus.comment = util::utf8ToUtf16(pbStatus.comment());
                      }
  
                      SymbolState visibility = deserializeVisibilityFromPb(pbStatus.visibility());
                      entry->symbolStatus.state = visibility;
  
                      if (visibility == SymbolState::kPublic) {
                          // This is a public symbol, we must encode the ID now if there is one.
                          if (pbEntry.has_id()) {
                              entry->id = static_cast<uint16_t>(pbEntry.id());
                          }
  
                          if (type->symbolStatus.state != SymbolState::kPublic) {
                              // If the type has not been made public, do so now.
                              type->symbolStatus.state = SymbolState::kPublic;
                              if (pbType.has_id()) {
                                  type->id = static_cast<uint8_t>(pbType.id());
                              }
                          }
                      } else if (visibility == SymbolState::kPrivate) {
                          if (type->symbolStatus.state == SymbolState::kUndefined) {
                              type->symbolStatus.state = SymbolState::kPrivate;
                          }
                      }
                  }
  
                  ResourceId resId(pbPackage.package_id(), pbType.id(), pbEntry.id());
                  if (resId.isValid()) {
                      idIndex[resId] = ResourceNameRef(pkg->name, type->type, entry->name);
                  }
  
                  for (const pb::ConfigValue& pbConfigValue : pbEntry.config_values()) {
                      const pb::ConfigDescription& pbConfig = pbConfigValue.config();
  
                      ConfigDescription config;
                      if (!deserializeConfigDescriptionFromPb(pbConfig, &config)) {
                          mDiag->error(DiagMessage(mSource) << "invalid configuration");
                          return {};
                      }
  
                      ResourceConfigValue* configValue = entry->findOrCreateValue(config,
                                                                                  pbConfig.product());
                      if (configValue->value) {
                          // Duplicate config.
                          mDiag->error(DiagMessage(mSource) << "duplicate configuration");
                          return {};
                      }
  
                      configValue->value = deserializeValueFromPb(pbConfigValue.value(),
                                                                  config, &table->stringPool);
                      if (!configValue->value) {
                          return {};
                      }
                  }
              }
          }
  
          ReferenceIdToNameVisitor visitor(&idIndex);
          visitAllValuesInPackage(pkg, &visitor);
          return true;
      }
  
  private:
      std::unique_ptr<Item> deserializeItemFromPb(const pb::Item& pbItem,
                                                  const ConfigDescription& config,
                                                  StringPool* pool) {
          if (pbItem.has_ref()) {
              const pb::Reference& pbRef = pbItem.ref();
              std::unique_ptr<Reference> ref = util::make_unique<Reference>();
              if (!deserializeReferenceFromPb(pbRef, ref.get())) {
                  return {};
              }
              return std::move(ref);
  
          } else if (pbItem.has_prim()) {
              const pb::Primitive& pbPrim = pbItem.prim();
              android::Res_value prim = {};
              prim.dataType = static_cast<uint8_t>(pbPrim.type());
              prim.data = pbPrim.data();
              return util::make_unique<BinaryPrimitive>(prim);
  
          } else if (pbItem.has_id()) {
              return util::make_unique<Id>();
  
          } else if (pbItem.has_str()) {
              const uint32_t idx = pbItem.str().idx();
              StringPiece16 str = util::getString(*mValuePool, idx);
  
              const android::ResStringPool_span* spans = mValuePool->styleAt(idx);
              if (spans && spans->name.index != android::ResStringPool_span::END) {
                  StyleString styleStr = { str.toString() };
                  while (spans->name.index != android::ResStringPool_span::END) {
                      styleStr.spans.push_back(Span{
                              util::getString(*mValuePool, spans->name.index).toString(),
                              spans->firstChar,
                              spans->lastChar
                      });
                      spans++;
                  }
                  return util::make_unique<StyledString>(
                          pool->makeRef(styleStr, StringPool::Context{ 1, config }));
              }
              return util::make_unique<String>(
                      pool->makeRef(str, StringPool::Context{ 1, config }));
  
          } else if (pbItem.has_raw_str()) {
              const uint32_t idx = pbItem.raw_str().idx();
              StringPiece16 str = util::getString(*mValuePool, idx);
              return util::make_unique<RawString>(
                      pool->makeRef(str, StringPool::Context{ 1, config }));
  
          } else if (pbItem.has_file()) {
              const uint32_t idx = pbItem.file().path_idx();
              StringPiece16 str = util::getString(*mValuePool, idx);
              return util::make_unique<FileReference>(
                      pool->makeRef(str, StringPool::Context{ 0, config }));
  
          } else {
              mDiag->error(DiagMessage(mSource) << "unknown item");
          }
          return {};
      }
  
      std::unique_ptr<Value> deserializeValueFromPb(const pb::Value& pbValue,
                                                    const ConfigDescription& config,
                                                    StringPool* pool) {
          const bool isWeak = pbValue.has_weak() ? pbValue.weak() : false;
  
          std::unique_ptr<Value> value;
          if (pbValue.has_item()) {
              value = deserializeItemFromPb(pbValue.item(), config, pool);
              if (!value) {
                  return {};
              }
  
          } else if (pbValue.has_compound_value()) {
              const pb::CompoundValue pbCompoundValue = pbValue.compound_value();
              if (pbCompoundValue.has_attr()) {
                  const pb::Attribute& pbAttr = pbCompoundValue.attr();
                  std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak);
                  attr->typeMask = pbAttr.format_flags();
                  for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) {
                      Attribute::Symbol symbol;
                      deserializeItemCommon(pbSymbol, &symbol.symbol);
                      if (!deserializeReferenceFromPb(pbSymbol.name(), &symbol.symbol)) {
                          return {};
                      }
                      symbol.value = pbSymbol.value();
                      attr->symbols.push_back(std::move(symbol));
                  }
                  value = std::move(attr);
  
              } else if (pbCompoundValue.has_style()) {
                  const pb::Style& pbStyle = pbCompoundValue.style();
                  std::unique_ptr<Style> style = util::make_unique<Style>();
                  if (pbStyle.has_parent()) {
                      style->parent = Reference();
                      if (!deserializeReferenceFromPb(pbStyle.parent(), &style->parent.value())) {
                          return {};
                      }
  
                      if (pbStyle.has_parent_source()) {
                          Source parentSource;
                          deserializeSourceFromPb(pbStyle.parent_source(), *mSourcePool,
                                                  &parentSource);
                          style->parent.value().setSource(std::move(parentSource));
                      }
                  }
  
                  for (const pb::Style_Entry& pbEntry : pbStyle.entries()) {
                      Style::Entry entry;
                      deserializeItemCommon(pbEntry, &entry.key);
                      if (!deserializeReferenceFromPb(pbEntry.key(), &entry.key)) {
                          return {};
                      }
  
                      entry.value = deserializeItemFromPb(pbEntry.item(), config, pool);
                      if (!entry.value) {
                          return {};
                      }
  
                      deserializeItemCommon(pbEntry, entry.value.get());
                      style->entries.push_back(std::move(entry));
                  }
                  value = std::move(style);
  
              } else if (pbCompoundValue.has_styleable()) {
                  const pb::Styleable& pbStyleable = pbCompoundValue.styleable();
                  std::unique_ptr<Styleable> styleable = util::make_unique<Styleable>();
                  for (const pb::Styleable_Entry& pbEntry : pbStyleable.entries()) {
                      Reference attrRef;
                      deserializeItemCommon(pbEntry, &attrRef);
                      deserializeReferenceFromPb(pbEntry.attr(), &attrRef);
                      styleable->entries.push_back(std::move(attrRef));
                  }
                  value = std::move(styleable);
  
              } else if (pbCompoundValue.has_array()) {
                  const pb::Array& pbArray = pbCompoundValue.array();
                  std::unique_ptr<Array> array = util::make_unique<Array>();
                  for (const pb::Array_Entry& pbEntry : pbArray.entries()) {
                      std::unique_ptr<Item> item = deserializeItemFromPb(pbEntry.item(), config,
                                                                         pool);
                      if (!item) {
                          return {};
                      }
  
                      deserializeItemCommon(pbEntry, item.get());
                      array->items.push_back(std::move(item));
                  }
                  value = std::move(array);
  
              } else if (pbCompoundValue.has_plural()) {
                  const pb::Plural& pbPlural = pbCompoundValue.plural();
                  std::unique_ptr<Plural> plural = util::make_unique<Plural>();
                  for (const pb::Plural_Entry& pbEntry : pbPlural.entries()) {
                      size_t pluralIdx = deserializePluralEnumFromPb(pbEntry.arity());
                      plural->values[pluralIdx] = deserializeItemFromPb(pbEntry.item(), config,
                                                                        pool);
                      if (!plural->values[pluralIdx]) {
                          return {};
                      }
  
                      deserializeItemCommon(pbEntry, plural->values[pluralIdx].get());
                  }
                  value = std::move(plural);
  
              } else {
                  mDiag->error(DiagMessage(mSource) << "unknown compound value");
                  return {};
              }
          } else {
              mDiag->error(DiagMessage(mSource) << "unknown value");
              return {};
          }
  
          assert(value && "forgot to set value");
  
          value->setWeak(isWeak);
          deserializeItemCommon(pbValue, value.get());
          return value;
      }
  
      bool deserializeReferenceFromPb(const pb::Reference& pbRef, Reference* outRef) {
          outRef->referenceType = deserializeReferenceTypeFromPb(pbRef.type());
          outRef->privateReference = pbRef.private_();
  
          if (!pbRef.has_id() && !pbRef.has_symbol_idx()) {
              return false;
          }
  
          if (pbRef.has_id()) {
              outRef->id = ResourceId(pbRef.id());
          }
  
          if (pbRef.has_symbol_idx()) {
              StringPiece16 strSymbol = util::getString(*mSymbolPool, pbRef.symbol_idx());
              ResourceNameRef nameRef;
              if (!ResourceUtils::parseResourceName(strSymbol, &nameRef, nullptr)) {
                  mDiag->error(DiagMessage(mSource) << "invalid reference name '"
                               << strSymbol << "'");
                  return false;
              }
  
              outRef->name = nameRef.toResourceName();
          }
          return true;
      }
  
      template <typename T>
      void deserializeItemCommon(const T& pbItem, Value* outValue) {
          if (pbItem.has_source()) {
              Source source;
              deserializeSourceFromPb(pbItem.source(), *mSourcePool, &source);
              outValue->setSource(std::move(source));
          }
  
          if (pbItem.has_comment()) {
              outValue->setComment(util::utf8ToUtf16(pbItem.comment()));
          }
      }
  
  private:
      const android::ResStringPool* mValuePool;
      const android::ResStringPool* mSourcePool;
      const android::ResStringPool* mSymbolPool;
      const Source mSource;
      IDiagnostics* mDiag;
  };
  
  } // namespace
  
  std::unique_ptr<ResourceTable> deserializeTableFromPb(const pb::ResourceTable& pbTable,
                                                        const Source& source,
                                                        IDiagnostics* diag) {
      // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
      // causes errors when qualifying it with android::
      using namespace android;
  
      std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
  
      if (!pbTable.has_string_pool()) {
          diag->error(DiagMessage(source) << "no string pool found");
          return {};
      }
  
      ResStringPool valuePool;
      status_t result = valuePool.setTo(pbTable.string_pool().data().data(),
                                        pbTable.string_pool().data().size());
      if (result != NO_ERROR) {
          diag->error(DiagMessage(source) << "invalid string pool");
          return {};
      }
  
      ResStringPool sourcePool;
      if (pbTable.has_source_pool()) {
          result = sourcePool.setTo(pbTable.source_pool().data().data(),
                                    pbTable.source_pool().data().size());
          if (result != NO_ERROR) {
              diag->error(DiagMessage(source) << "invalid source pool");
              return {};
          }
      }
  
      ResStringPool symbolPool;
      if (pbTable.has_symbol_pool()) {
          result = symbolPool.setTo(pbTable.symbol_pool().data().data(),
                                    pbTable.symbol_pool().data().size());
          if (result != NO_ERROR) {
              diag->error(DiagMessage(source) << "invalid symbol pool");
              return {};
          }
      }
  
      PackagePbDeserializer packagePbDeserializer(&valuePool, &sourcePool, &symbolPool, source, diag);
      for (const pb::Package& pbPackage : pbTable.packages()) {
          if (!packagePbDeserializer.deserializeFromPb(pbPackage, table.get())) {
              return {};
          }
      }
      return table;
  }
  
  std::unique_ptr<ResourceFile> deserializeCompiledFileFromPb(const pb::CompiledFile& pbFile,
                                                              const Source& source,
                                                              IDiagnostics* diag) {
      std::unique_ptr<ResourceFile> file = util::make_unique<ResourceFile>();
  
      ResourceNameRef nameRef;
  
      // Need to create an lvalue here so that nameRef can point to something real.
      std::u16string utf16Name = util::utf8ToUtf16(pbFile.resource_name());
      if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
          diag->error(DiagMessage(source) << "invalid resource name in compiled file header: "
                      << pbFile.resource_name());
          return {};
      }
      file->name = nameRef.toResourceName();
      file->source.path = pbFile.source_path();
      deserializeConfigDescriptionFromPb(pbFile.config(), &file->config);
  
      for (const pb::CompiledFile_Symbol& pbSymbol : pbFile.exported_symbols()) {
          // Need to create an lvalue here so that nameRef can point to something real.
          utf16Name = util::utf8ToUtf16(pbSymbol.resource_name());
          if (!ResourceUtils::parseResourceName(utf16Name, &nameRef)) {
              diag->error(DiagMessage(source) << "invalid resource name for exported symbol in "
                                                 "compiled file header: "
                                              << pbFile.resource_name());
              return {};
          }
          file->exportedSymbols.push_back(
                  SourcedResourceName{ nameRef.toResourceName(), pbSymbol.line_no() });
      }
      return file;
  }
  
  CompiledFileInputStream::CompiledFileInputStream(const void* data, size_t size) :
          mIn(static_cast<const uint8_t*>(data), size), mPbFile(),
          mData(static_cast<const uint8_t*>(data)), mSize(size) {
  }
  
  const pb::CompiledFile* CompiledFileInputStream::CompiledFile() {
      if (!mPbFile) {
          std::unique_ptr<pb::CompiledFile> pbFile = util::make_unique<pb::CompiledFile>();
          uint64_t pbSize = 0u;
          if (!mIn.ReadLittleEndian64(&pbSize)) {
              return nullptr;
          }
          mIn.PushLimit(static_cast<int>(pbSize));
          if (!pbFile->ParsePartialFromCodedStream(&mIn)) {
              return nullptr;
          }
  
          const size_t padding = 4 - (pbSize & 0x03);
          const size_t offset = sizeof(uint64_t) + pbSize + padding;
          if (offset > mSize) {
              return nullptr;
          }
  
          mData += offset;
          mSize -= offset;
          mPbFile = std::move(pbFile);
      }
      return mPbFile.get();
  }
  
  const void* CompiledFileInputStream::data() {
      if (!mPbFile) {
          if (!CompiledFile()) {
              return nullptr;
          }
      }
      return mData;
  }
  
  size_t CompiledFileInputStream::size() {
      if (!mPbFile) {
          if (!CompiledFile()) {
              return 0;
          }
      }
      return mSize;
  }
  
  } // namespace aapt
  