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