Home | History | Annotate | Download | only in gn
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "tools/gn/file_template.h"
      6 
      7 #include "tools/gn/filesystem_utils.h"
      8 
      9 const char FileTemplate::kSource[] = "{{source}}";
     10 const char FileTemplate::kSourceNamePart[] = "{{source_name_part}}";
     11 
     12 FileTemplate::FileTemplate(const Value& t, Err* err) {
     13   ParseInput(t, err);
     14 }
     15 
     16 FileTemplate::FileTemplate(const std::vector<std::string>& t) {
     17   for (size_t i = 0; i < t.size(); i++)
     18     ParseOneTemplateString(t[i]);
     19 }
     20 
     21 FileTemplate::~FileTemplate() {
     22 }
     23 
     24 void FileTemplate::Apply(const Value& sources,
     25                          const ParseNode* origin,
     26                          std::vector<Value>* dest,
     27                          Err* err) const {
     28   if (!sources.VerifyTypeIs(Value::LIST, err))
     29     return;
     30   dest->reserve(sources.list_value().size() * templates_.container().size());
     31 
     32   // Temporary holding place, allocate outside to re-use- buffer.
     33   std::vector<std::string> string_output;
     34 
     35   const std::vector<Value>& sources_list = sources.list_value();
     36   for (size_t i = 0; i < sources_list.size(); i++) {
     37     if (!sources_list[i].VerifyTypeIs(Value::STRING, err))
     38       return;
     39 
     40     ApplyString(sources_list[i].string_value(), &string_output);
     41     for (size_t out_i = 0; out_i < string_output.size(); out_i++)
     42       dest->push_back(Value(origin, string_output[i]));
     43   }
     44 }
     45 
     46 void FileTemplate::ApplyString(const std::string& str,
     47                                std::vector<std::string>* output) const {
     48   // Compute all substitutions needed so we can just do substitutions below.
     49   // We skip the LITERAL one since that varies each time.
     50   std::string subst[Subrange::NUM_TYPES];
     51   if (types_required_[Subrange::SOURCE])
     52     subst[Subrange::SOURCE] = str;
     53   if (types_required_[Subrange::NAME_PART])
     54     subst[Subrange::NAME_PART] = FindFilenameNoExtension(&str).as_string();
     55 
     56   output->resize(templates_.container().size());
     57   for (size_t template_i = 0;
     58        template_i < templates_.container().size(); template_i++) {
     59     const Template& t = templates_[template_i];
     60     (*output)[template_i].clear();
     61     for (size_t subrange_i = 0; subrange_i < t.container().size();
     62          subrange_i++) {
     63       if (t[subrange_i].type == Subrange::LITERAL)
     64         (*output)[template_i].append(t[subrange_i].literal);
     65       else
     66         (*output)[template_i].append(subst[t[subrange_i].type]);
     67     }
     68   }
     69 }
     70 
     71 void FileTemplate::ParseInput(const Value& value, Err* err) {
     72   switch (value.type()) {
     73     case Value::STRING:
     74       ParseOneTemplateString(value.string_value());
     75       break;
     76     case Value::LIST:
     77       for (size_t i = 0; i < value.list_value().size(); i++) {
     78         if (!value.list_value()[i].VerifyTypeIs(Value::STRING, err))
     79           return;
     80         ParseOneTemplateString(value.list_value()[i].string_value());
     81       }
     82       break;
     83     default:
     84       *err = Err(value, "File template must be a string or list.",
     85                  "A sarcastic comment about your skills goes here.");
     86   }
     87 }
     88 
     89 void FileTemplate::ParseOneTemplateString(const std::string& str) {
     90   templates_.container().resize(templates_.container().size() + 1);
     91   Template& t = templates_[templates_.container().size() - 1];
     92 
     93   size_t cur = 0;
     94   while (true) {
     95     size_t next = str.find("{{", cur);
     96 
     97     // Pick up everything from the previous spot to here as a literal.
     98     if (next == std::string::npos) {
     99       if (cur != str.size())
    100         t.container().push_back(Subrange(Subrange::LITERAL, str.substr(cur)));
    101       break;
    102     } else if (next > cur) {
    103       t.container().push_back(
    104           Subrange(Subrange::LITERAL, str.substr(cur, next - cur)));
    105     }
    106 
    107     // Decode the template param.
    108     if (str.compare(next, arraysize(kSource) - 1, kSource) == 0) {
    109       t.container().push_back(Subrange(Subrange::SOURCE));
    110       types_required_[Subrange::SOURCE] = true;
    111       cur = next + arraysize(kSource) - 1;
    112     } else if (str.compare(next, arraysize(kSourceNamePart) - 1,
    113                            kSourceNamePart) == 0) {
    114       t.container().push_back(Subrange(Subrange::NAME_PART));
    115       types_required_[Subrange::NAME_PART] = true;
    116       cur = next + arraysize(kSourceNamePart) - 1;
    117     } else {
    118       // If it's not a match, treat it like a one-char literal (this will be
    119       // rare, so it's not worth the bother to add to the previous literal) so
    120       // we can keep going.
    121       t.container().push_back(Subrange(Subrange::LITERAL, "{"));
    122       cur = next + 1;
    123     }
    124   }
    125 }
    126