Brian Silverman | 70325d6 | 2015-09-20 17:00:43 -0400 | [diff] [blame] | 1 | // Copyright (c) 2006, Google Inc. |
| 2 | // All rights reserved. |
| 3 | // |
| 4 | // Redistribution and use in source and binary forms, with or without |
| 5 | // modification, are permitted provided that the following conditions are |
| 6 | // met: |
| 7 | // |
| 8 | // * Redistributions of source code must retain the above copyright |
| 9 | // notice, this list of conditions and the following disclaimer. |
| 10 | // * Redistributions in binary form must reproduce the above |
| 11 | // copyright notice, this list of conditions and the following disclaimer |
| 12 | // in the documentation and/or other materials provided with the |
| 13 | // distribution. |
| 14 | // * Neither the name of Google Inc. nor the names of its |
| 15 | // contributors may be used to endorse or promote products derived from |
| 16 | // this software without specific prior written permission. |
| 17 | // |
| 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 19 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 20 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 21 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 22 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 23 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 24 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 25 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 26 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 27 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 28 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 29 | |
| 30 | // --- |
| 31 | // Author: csilvers@google.com (Craig Silverstein) |
| 32 | // |
| 33 | // Based on the 'old' TemplateDictionary by Frank Jernigan. |
| 34 | |
| 35 | #include <config.h> |
| 36 | #include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE |
| 37 | #include <assert.h> |
| 38 | #include <stdlib.h> |
| 39 | #include <stdio.h> |
| 40 | #include <stdarg.h> |
| 41 | #include <algorithm> // for sort() |
| 42 | #include HASH_MAP_H |
| 43 | #include <map> |
| 44 | #include <string> |
| 45 | #include <utility> // for pair<> |
| 46 | #include <vector> |
| 47 | |
| 48 | #include "base/arena-inl.h" |
| 49 | #include "base/thread_annotations.h" |
| 50 | #include "indented_writer.h" |
| 51 | #include <ctemplate/find_ptr.h> |
| 52 | #include <ctemplate/template_dictionary.h> |
| 53 | #include <ctemplate/template_modifiers.h> |
| 54 | #include "base/small_map.h" |
| 55 | #include "base/util.h" // for DCHECK |
| 56 | |
| 57 | using std::vector; |
| 58 | using std::string; |
| 59 | using std::map; |
| 60 | using std::pair; |
| 61 | using std::make_pair; |
| 62 | |
| 63 | namespace ctemplate { |
| 64 | |
| 65 | // Guards the initialization of the global dictionary. |
| 66 | static GoogleOnceType g_once = GOOGLE_ONCE_INIT; |
| 67 | // Guard access to the global dictionary. |
| 68 | static Mutex g_static_mutex(base::LINKER_INITIALIZED); |
| 69 | |
| 70 | /*static*/ UnsafeArena* const TemplateDictionary::NO_ARENA = NULL; |
| 71 | /*static*/ TemplateDictionary::GlobalDict* TemplateDictionary::global_dict_ |
| 72 | GUARDED_BY(g_static_mutex) PT_GUARDED_BY(g_static_mutex) = NULL; |
| 73 | /*static*/ TemplateString* TemplateDictionary::empty_string_ = NULL; |
| 74 | |
| 75 | |
| 76 | static const char* const kAnnotateOutput = "__ctemplate_annotate_output__"; |
| 77 | |
| 78 | // ---------------------------------------------------------------------- |
| 79 | // TemplateDictionary::map_arena_init |
| 80 | // This class is what small_map<> uses to create a new |
| 81 | // arena-allocated map<> when it decides it needs to do that. |
| 82 | // ---------------------------------------------------------------------- |
| 83 | |
| 84 | class TemplateDictionary::map_arena_init { |
| 85 | public: |
| 86 | map_arena_init(UnsafeArena* arena) : arena_(arena) { } |
| 87 | template<typename T> void operator ()(ManualConstructor<T>* map) const { |
| 88 | map->Init(typename T::key_compare(), arena_); |
| 89 | } |
| 90 | private: |
| 91 | UnsafeArena* arena_; |
| 92 | }; |
| 93 | |
| 94 | // ---------------------------------------------------------------------- |
| 95 | // TemplateDictionary::LazilyCreateDict() |
| 96 | // TemplateDictionary::LazilyCreateTemplateGlobalDict() |
| 97 | // TemplateDictionary::CreateDictVector() |
| 98 | // TemplateDictionary::CreateTemplateSubdict() |
| 99 | // These routines allocate the objects that TemplateDictionary |
| 100 | // allocates (sub-dictionaries, variable maps, etc). Each |
| 101 | // allocates memory on the arena, and instructs the STL objects |
| 102 | // to use the arena for their own internal allocations as well. |
| 103 | // ---------------------------------------------------------------------- |
| 104 | |
| 105 | template<typename T> |
| 106 | inline void TemplateDictionary::LazilyCreateDict(T** dict) { |
| 107 | if (*dict != NULL) |
| 108 | return; |
| 109 | // Placement new: construct the map in the memory used by *dict. |
| 110 | void* buffer = arena_->AllocAligned(sizeof(**dict), |
| 111 | BaseArena::kDefaultAlignment); |
| 112 | new (buffer) T(arena_); |
| 113 | *dict = reinterpret_cast<T*>(buffer); |
| 114 | } |
| 115 | |
| 116 | inline void TemplateDictionary::LazyCreateTemplateGlobalDict() { |
| 117 | if (!template_global_dict_owner_->template_global_dict_) { |
| 118 | template_global_dict_owner_->template_global_dict_ = |
| 119 | CreateTemplateSubdict("Template Globals", arena_, |
| 120 | template_global_dict_owner_, |
| 121 | template_global_dict_owner_); |
| 122 | } |
| 123 | } |
| 124 | |
| 125 | inline TemplateDictionary::DictVector* TemplateDictionary::CreateDictVector() { |
| 126 | void* buffer = arena_->AllocAligned(sizeof(DictVector), |
| 127 | BaseArena::kDefaultAlignment); |
| 128 | // Placement new: construct the vector in the memory used by buffer. |
| 129 | new (buffer) DictVector(arena_); |
| 130 | return reinterpret_cast<DictVector*>(buffer); |
| 131 | } |
| 132 | |
| 133 | inline TemplateDictionary* TemplateDictionary::CreateTemplateSubdict( |
| 134 | const TemplateString& name, |
| 135 | UnsafeArena* arena, |
| 136 | TemplateDictionary* parent_dict, |
| 137 | TemplateDictionary* template_global_dict_owner) { |
| 138 | void* buffer = arena->AllocAligned(sizeof(TemplateDictionary), |
| 139 | BaseArena::kDefaultAlignment); |
| 140 | // Placement new: construct the sub-tpl in the memory used by tplbuf. |
| 141 | new (buffer) TemplateDictionary(name, arena, parent_dict, |
| 142 | template_global_dict_owner); |
| 143 | return reinterpret_cast<TemplateDictionary*>(buffer); |
| 144 | } |
| 145 | |
| 146 | |
| 147 | // ---------------------------------------------------------------------- |
| 148 | // TemplateDictionary::HashInsert() |
| 149 | // A convenience function that's equivalent to m[key] = value, but |
| 150 | // converting the key to an id first, and without necessarily needing |
| 151 | // key to have a default constructor like operator[] does. It also |
| 152 | // inserts (key, id(key)) into a map to allow for id->key mapping. |
| 153 | // ---------------------------------------------------------------------- |
| 154 | |
| 155 | // By default, prefer the m[key] = value construct. We do something |
| 156 | // more complex for TemplateString, though, since m[key] requires a |
| 157 | // zero-arg constructor, which TemplateString doesn't have. We could |
| 158 | // do the more complex thing everywhere, but that seems to trigger a |
| 159 | // bug in in gcc 4.1.2 (at least) when compiled with -O2. Shrug. |
| 160 | namespace { |
| 161 | template<typename MapType, typename ValueType> |
| 162 | inline void DoHashInsert(MapType* m, TemplateId id, ValueType value) { |
| 163 | (*m)[id] = value; |
| 164 | } |
| 165 | |
| 166 | template<typename MapType> |
| 167 | inline void DoHashInsert(MapType* m, TemplateId id, TemplateString value) { |
| 168 | pair<typename MapType::iterator, bool> r |
| 169 | = m->insert(typename MapType::value_type(id, value)); |
| 170 | // Unfortunately, insert() doesn't actually replace if key is |
| 171 | // already in the map. Thus, in that case (insert().second == false), |
| 172 | // we need to overwrite the old value. Since TemplateString |
| 173 | // doesn't define operator=, the easiest legal way to overwrite is |
| 174 | // to use the copy-constructor with placement-new. Note that since |
| 175 | // TemplateString has no destructor, we don't need to call the |
| 176 | // destructor to 'clear out' the old value. |
| 177 | if (r.second == false) { |
| 178 | new (&r.first->second) TemplateString(value); |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | template<typename MapType, typename ValueType> |
| 184 | void TemplateDictionary::HashInsert(MapType* m, |
| 185 | TemplateString key, ValueType value) { |
| 186 | const TemplateId id = key.GetGlobalId(); |
| 187 | DoHashInsert(m, id, value); |
| 188 | AddToIdToNameMap(id, key); // allows us to do the hash-key -> name mapping |
| 189 | } |
| 190 | |
| 191 | // ---------------------------------------------------------------------- |
| 192 | // TemplateDictionary::SetupGlobalDict() |
| 193 | // Must be called exactly once before accessing global_dict_. |
| 194 | // GoogleOnceInit() is used to manage that initialization in a thread-safe |
| 195 | // way. |
| 196 | // ---------------------------------------------------------------------- |
| 197 | /*static*/ void TemplateDictionary::SetupGlobalDict() |
| 198 | NO_THREAD_SAFETY_ANALYSIS { |
| 199 | global_dict_ = new TemplateDictionary::GlobalDict; |
| 200 | // Initialize the built-ins |
| 201 | HashInsert(global_dict_, TemplateString("BI_SPACE"), TemplateString(" ")); |
| 202 | HashInsert(global_dict_, TemplateString("BI_NEWLINE"), TemplateString("\n")); |
| 203 | // This is used for name-lookup misses. |
| 204 | empty_string_ = new TemplateString(""); |
| 205 | } |
| 206 | |
| 207 | // ---------------------------------------------------------------------- |
| 208 | // TemplateDictionary::TemplateDictionary() |
| 209 | // TemplateDictionary::~TemplateDictionary() |
| 210 | // The only tricky thing is that we make sure the static vars are |
| 211 | // set up properly. This must be done at each construct time, |
| 212 | // because it's the responsibility of the first dictionary created |
| 213 | // in the program to set up the globals, and that could be us. |
| 214 | // The UnsafeArena() argument is how big to make each arena |
| 215 | // block. Too big and space is wasted. Too small and we spend |
| 216 | // a lot of time allocating new arena blocks. 32k seems right. |
| 217 | // ---------------------------------------------------------------------- |
| 218 | |
| 219 | TemplateDictionary::TemplateDictionary(const TemplateString& name, |
| 220 | UnsafeArena* arena) |
| 221 | : arena_(arena ? arena : new UnsafeArena(32768)), |
| 222 | should_delete_arena_(arena ? false : true), // true if we called new |
| 223 | name_(Memdup(name)), // arena must have been set up first |
| 224 | variable_dict_(NULL), |
| 225 | section_dict_(NULL), |
| 226 | include_dict_(NULL), |
| 227 | template_global_dict_(NULL), |
| 228 | template_global_dict_owner_(this), |
| 229 | parent_dict_(NULL), |
| 230 | filename_(NULL) { |
| 231 | GoogleOnceInit(&g_once, &SetupGlobalDict); |
| 232 | } |
| 233 | |
| 234 | TemplateDictionary::TemplateDictionary( |
| 235 | const TemplateString& name, |
| 236 | UnsafeArena* arena, |
| 237 | TemplateDictionary* parent_dict, |
| 238 | TemplateDictionary* template_global_dict_owner) |
| 239 | : arena_(arena), should_delete_arena_(false), // parents own it |
| 240 | name_(Memdup(name)), // arena must have been set up first |
| 241 | variable_dict_(NULL), |
| 242 | section_dict_(NULL), |
| 243 | include_dict_(NULL), |
| 244 | template_global_dict_(NULL), |
| 245 | template_global_dict_owner_(template_global_dict_owner), |
| 246 | parent_dict_(parent_dict), |
| 247 | filename_(NULL) { |
| 248 | assert(template_global_dict_owner_ != NULL); |
| 249 | GoogleOnceInit(&g_once, &SetupGlobalDict); |
| 250 | } |
| 251 | |
| 252 | TemplateDictionary::~TemplateDictionary() { |
| 253 | // Everything we allocate, we allocate on the arena, so we |
| 254 | // don't need to free anything here. |
| 255 | if (should_delete_arena_) { |
| 256 | delete arena_; |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | // ---------------------------------------------------------------------- |
| 261 | // TemplateDictionary::MakeCopy() |
| 262 | // Makes a recursive copy: so we copy any include dictionaries and |
| 263 | // section dictionaries we see as well. InternalMakeCopy() is |
| 264 | // needed just so we can ensure that if we're doing a copy of a |
| 265 | // subtree, it's due to a recursive call. Returns NULL if there |
| 266 | // is an error copying. |
| 267 | // ---------------------------------------------------------------------- |
| 268 | |
| 269 | TemplateDictionary* TemplateDictionary::InternalMakeCopy( |
| 270 | const TemplateString& name_of_copy, |
| 271 | UnsafeArena* arena, |
| 272 | TemplateDictionary* parent_dict, |
| 273 | TemplateDictionary* template_global_dict_owner) { |
| 274 | |
| 275 | TemplateDictionary* newdict; |
| 276 | if (template_global_dict_owner_ == this) { |
| 277 | // We're a root-level template. We want the copy to be just like |
| 278 | // us, and have its own template_global_dict_, that it owns. |
| 279 | // We use the normal global new, since newdict will be returned |
| 280 | // to the user. |
| 281 | newdict = new TemplateDictionary(name_of_copy, arena); |
| 282 | } else { // recursive calls use private contructor |
| 283 | // We're not a root-level template, so we want the copy to refer to the |
| 284 | // same template_global_dict_ owner that we do. |
| 285 | // Note: we always use our own arena, even when we have a parent |
| 286 | // (though we have the same arena as our parent when we have one). |
| 287 | assert(arena); |
| 288 | assert(parent_dict ? arena == parent_dict->arena_ : true); |
| 289 | newdict = CreateTemplateSubdict(name_of_copy, arena, |
| 290 | parent_dict, template_global_dict_owner); |
| 291 | } |
| 292 | |
| 293 | // Copy the variable dictionary |
| 294 | if (variable_dict_) { |
| 295 | newdict->LazilyCreateDict(&newdict->variable_dict_); |
| 296 | for (VariableDict::const_iterator it = variable_dict_->begin(); |
| 297 | it != variable_dict_->end(); ++it) { |
| 298 | newdict->variable_dict_->insert(make_pair(it->first, |
| 299 | newdict->Memdup(it->second))); |
| 300 | } |
| 301 | } |
| 302 | // ...and the template-global-dict, if we have one (only root-level tpls do) |
| 303 | if (template_global_dict_) { |
| 304 | newdict->template_global_dict_ = template_global_dict_->InternalMakeCopy( |
| 305 | template_global_dict_->name(), newdict->arena_, newdict, |
| 306 | newdict->template_global_dict_owner_); |
| 307 | } |
| 308 | // Copy the section dictionary |
| 309 | if (section_dict_) { |
| 310 | newdict->LazilyCreateDict(&newdict->section_dict_); |
| 311 | for (SectionDict::iterator it = section_dict_->begin(); |
| 312 | it != section_dict_->end(); ++it) { |
| 313 | DictVector* dicts = newdict->CreateDictVector(); |
| 314 | newdict->section_dict_->insert(make_pair(it->first, dicts)); |
| 315 | for (DictVector::iterator it2 = it->second->begin(); |
| 316 | it2 != it->second->end(); ++it2) { |
| 317 | TemplateDictionary* subdict = *it2; |
| 318 | // In this case, we pass in newdict as the parent of our new dict. |
| 319 | dicts->push_back(subdict->InternalMakeCopy( |
| 320 | subdict->name(), newdict->arena_, |
| 321 | newdict, newdict->template_global_dict_owner_)); |
| 322 | } |
| 323 | } |
| 324 | } |
| 325 | // Copy the includes-dictionary |
| 326 | if (include_dict_) { |
| 327 | newdict->LazilyCreateDict(&newdict->include_dict_); |
| 328 | for (IncludeDict::iterator it = include_dict_->begin(); |
| 329 | it != include_dict_->end(); ++it) { |
| 330 | DictVector* dicts = newdict->CreateDictVector(); |
| 331 | newdict->include_dict_->insert(make_pair(it->first, dicts)); |
| 332 | for (DictVector::iterator it2 = it->second->begin(); |
| 333 | it2 != it->second->end(); ++it2) { |
| 334 | TemplateDictionary* subdict = *it2; |
| 335 | // In this case, we pass in NULL as the parent of our new dict: |
| 336 | // parents are not inherited across include-dictionaries. |
| 337 | dicts->push_back(subdict->InternalMakeCopy( |
| 338 | subdict->name(), newdict->arena_, |
| 339 | NULL, newdict->template_global_dict_owner_)); |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | // Finally, copy everything else not set properly by the constructor |
| 345 | newdict->filename_ = newdict->Memdup(filename_).ptr_; |
| 346 | |
| 347 | return newdict; |
| 348 | } |
| 349 | |
| 350 | TemplateDictionary* TemplateDictionary::MakeCopy( |
| 351 | const TemplateString& name_of_copy, UnsafeArena* arena) { |
| 352 | if (template_global_dict_owner_ != this) { |
| 353 | // We're not at the root, which is illegal. |
| 354 | return NULL; |
| 355 | } |
| 356 | return InternalMakeCopy(name_of_copy, arena, |
| 357 | NULL, template_global_dict_owner_); |
| 358 | } |
| 359 | |
| 360 | |
| 361 | // ---------------------------------------------------------------------- |
| 362 | // TemplateDictionary::StringAppendV() |
| 363 | // Does an snprintf to a string. Idea is to grow string as needed. |
| 364 | // Writes to space if possible -- caller must ensure space has |
| 365 | // size at least 1024 -- and if not allocates a buffer of its |
| 366 | // own which the caller must free. Sets out to the buffer written |
| 367 | // to (space or something else). Returns the number of bytes |
| 368 | // written into out. |
| 369 | // ---------------------------------------------------------------------- |
| 370 | |
| 371 | int TemplateDictionary::StringAppendV(char* space, char** out, |
| 372 | const char* format, va_list ap) { |
| 373 | const int kBufsize = 1024; |
| 374 | // It's possible for methods that use a va_list to invalidate |
| 375 | // the data in it upon use. The fix is to make a copy |
| 376 | // of the structure before using it and use that copy instead. |
| 377 | va_list backup_ap; |
| 378 | va_copy(backup_ap, ap); |
| 379 | int result = vsnprintf(space, kBufsize, format, backup_ap); |
| 380 | va_end(backup_ap); |
| 381 | |
| 382 | if ((result >= 0) && (result < kBufsize)) { |
| 383 | *out = space; |
| 384 | return result; // It fit |
| 385 | } |
| 386 | |
| 387 | // Repeatedly increase buffer size until it fits |
| 388 | int length = kBufsize; |
| 389 | while (true) { |
| 390 | if (result < 0) { |
| 391 | // Older snprintf() behavior. :-( Just try doubling the buffer size |
| 392 | length *= 2; |
| 393 | } else { |
| 394 | // We need exactly "result+1" characters |
| 395 | length = result+1; |
| 396 | } |
| 397 | char* buf = new char[length]; |
| 398 | |
| 399 | // Restore the va_list before we use it again |
| 400 | va_copy(backup_ap, ap); |
| 401 | result = vsnprintf(buf, length, format, backup_ap); |
| 402 | va_end(backup_ap); |
| 403 | |
| 404 | if ((result >= 0) && (result < length)) { |
| 405 | *out = buf; |
| 406 | return result; |
| 407 | } |
| 408 | delete[] buf; |
| 409 | } |
| 410 | } |
| 411 | |
| 412 | // ---------------------------------------------------------------------- |
| 413 | // TemplateDictionary::SetValue() |
| 414 | // TemplateDictionary::SetIntValue() |
| 415 | // TemplateDictionary::SetFormattedValue() |
| 416 | // TemplateDictionary::SetEscapedValue() |
| 417 | // TemplateDictionary::SetEscapedFormattedValue() |
| 418 | // The functions to set the value of a variable. For each, |
| 419 | // I first define the char*+length version. Then, after those |
| 420 | // five definitions, I define a zillion alternate versions: |
| 421 | // strings, char*s, etc. The only non-obvious thing about |
| 422 | // each function is I make sure to copy both key and value to |
| 423 | // the arena, so we have our own, persistent copy of them. |
| 424 | // ---------------------------------------------------------------------- |
| 425 | |
| 426 | void TemplateDictionary::SetValue(const TemplateString variable, |
| 427 | const TemplateString value) { |
| 428 | LazilyCreateDict(&variable_dict_); |
| 429 | HashInsert(variable_dict_, variable, Memdup(value)); |
| 430 | } |
| 431 | |
| 432 | void TemplateDictionary::SetValueWithoutCopy(const TemplateString variable, |
| 433 | const TemplateString value) { |
| 434 | LazilyCreateDict(&variable_dict_); |
| 435 | // Don't memdup value - the caller will manage memory. |
| 436 | HashInsert(variable_dict_, variable, value); |
| 437 | } |
| 438 | |
| 439 | void TemplateDictionary::SetIntValue(const TemplateString variable, |
| 440 | long value) { |
| 441 | char buffer[64]; // big enough for any int |
| 442 | int valuelen = snprintf(buffer, sizeof(buffer), "%ld", value); |
| 443 | LazilyCreateDict(&variable_dict_); |
| 444 | HashInsert(variable_dict_, variable, Memdup(buffer, valuelen)); |
| 445 | } |
| 446 | |
| 447 | void TemplateDictionary::SetFormattedValue(const TemplateString variable, |
| 448 | const char* format, ...) { |
| 449 | char* buffer; |
| 450 | |
| 451 | char* scratch = arena_->Alloc(1024); // StringAppendV requires >=1024 bytes |
| 452 | va_list ap; |
| 453 | va_start(ap, format); |
| 454 | const int buflen = StringAppendV(scratch, &buffer, format, ap); |
| 455 | va_end(ap); |
| 456 | |
| 457 | LazilyCreateDict(&variable_dict_); |
| 458 | |
| 459 | // If it fit into scratch, great, otherwise we need to copy into arena |
| 460 | if (buffer == scratch) { |
| 461 | scratch = arena_->Shrink(scratch, buflen+1); // from 1024 to |value+\0| |
| 462 | HashInsert(variable_dict_, variable, TemplateString(scratch, buflen)); |
| 463 | } else { |
| 464 | arena_->Shrink(scratch, 0); // reclaim arena space we didn't use |
| 465 | HashInsert(variable_dict_, variable, Memdup(buffer, buflen)); |
| 466 | delete[] buffer; |
| 467 | } |
| 468 | } |
| 469 | |
| 470 | void TemplateDictionary::SetEscapedValue(TemplateString variable, |
| 471 | TemplateString value, |
| 472 | const TemplateModifier& escfn) { |
| 473 | SetValue(variable, string(escfn(value.data(), value.size()))); |
| 474 | } |
| 475 | |
| 476 | void TemplateDictionary::SetEscapedFormattedValue(TemplateString variable, |
| 477 | const TemplateModifier& escfn, |
| 478 | const char* format, ...) { |
| 479 | char* buffer; |
| 480 | |
| 481 | char* scratch = arena_->Alloc(1024); // StringAppendV requires >=1024 bytes |
| 482 | va_list ap; |
| 483 | va_start(ap, format); |
| 484 | const int buflen = StringAppendV(scratch, &buffer, format, ap); |
| 485 | va_end(ap); |
| 486 | |
| 487 | string escaped_string(escfn(buffer, buflen)); |
| 488 | // Reclaim the arena space: the value we care about is now in escaped_string |
| 489 | arena_->Shrink(scratch, 0); // reclaim arena space we didn't use |
| 490 | if (buffer != scratch) |
| 491 | delete[] buffer; |
| 492 | |
| 493 | SetValue(variable, escaped_string); |
| 494 | } |
| 495 | |
| 496 | // ---------------------------------------------------------------------- |
| 497 | // TemplateDictionary::SetTemplateGlobalValue() |
| 498 | // Sets a value in the template-global dict. Unlike normal |
| 499 | // variable lookups, these persist across sub-includes. |
| 500 | // ---------------------------------------------------------------------- |
| 501 | |
| 502 | void TemplateDictionary::SetTemplateGlobalValue(const TemplateString variable, |
| 503 | const TemplateString value) { |
| 504 | assert(template_global_dict_owner_ != NULL); |
| 505 | LazyCreateTemplateGlobalDict(); |
| 506 | template_global_dict_owner_->template_global_dict_->SetValue(variable, value); |
| 507 | } |
| 508 | |
| 509 | void TemplateDictionary::SetTemplateGlobalValueWithoutCopy( |
| 510 | const TemplateString variable, |
| 511 | const TemplateString value) { |
| 512 | assert(template_global_dict_owner_ != NULL); |
| 513 | LazyCreateTemplateGlobalDict(); |
| 514 | // Don't memdup value - the caller will manage memory. |
| 515 | template_global_dict_owner_->template_global_dict_-> |
| 516 | SetValueWithoutCopy(variable, value); |
| 517 | } |
| 518 | |
| 519 | // ---------------------------------------------------------------------- |
| 520 | // TemplateDictionary::SetGlobalValue() |
| 521 | // Sets a value in the global dict. Note this is a static method. |
| 522 | // ---------------------------------------------------------------------- |
| 523 | |
| 524 | /*static*/ void TemplateDictionary::SetGlobalValue( |
| 525 | const TemplateString variable, |
| 526 | const TemplateString value) LOCKS_EXCLUDED(g_static_mutex) { |
| 527 | // We can't use memdup here, since we're a static method. We do a strdup, |
| 528 | // which is fine, since global_dict_ lives the entire program anyway. |
| 529 | // It's unnecessary to copy the variable, since HashInsert takes care of |
| 530 | // that for us. |
| 531 | char* value_copy = new char[value.length_ + 1]; |
| 532 | memcpy(value_copy, value.ptr_, value.length_); |
| 533 | value_copy[value.length_] = '\0'; |
| 534 | |
| 535 | GoogleOnceInit(&g_once, &SetupGlobalDict); |
| 536 | |
| 537 | MutexLock ml(&g_static_mutex); |
| 538 | HashInsert(global_dict_, |
| 539 | variable, |
| 540 | TemplateString(value_copy, value.length_)); |
| 541 | } |
| 542 | |
| 543 | // ---------------------------------------------------------------------- |
| 544 | // TemplateDictionary::AddSectionDictionary() |
| 545 | // TemplateDictionary::ShowSection() |
| 546 | // TemplateDictionary::ShowTemplateGlobalSection() |
| 547 | // The new dictionary starts out empty, with us as the parent. |
| 548 | // It shares our arena. The name is constructed out of our |
| 549 | // name plus the section name. ShowSection() is the equivalent |
| 550 | // to AddSectionDictionary("empty_dict"). |
| 551 | // ---------------------------------------------------------------------- |
| 552 | |
| 553 | /*static*/ string TemplateDictionary::CreateSubdictName( |
| 554 | const TemplateString& dict_name, const TemplateString& sub_name, |
| 555 | size_t index, const char* suffix) { |
| 556 | char index_str[64]; |
| 557 | snprintf(index_str, sizeof(index_str), "%" PRIuS, index); |
| 558 | return (PrintableTemplateString(dict_name) + "/" + |
| 559 | PrintableTemplateString(sub_name) + "#" + index_str + suffix); |
| 560 | } |
| 561 | |
| 562 | TemplateDictionary* TemplateDictionary::AddSectionDictionary( |
| 563 | const TemplateString section_name) { |
| 564 | LazilyCreateDict(§ion_dict_); |
| 565 | DictVector* dicts = find_ptr2(*section_dict_, section_name.GetGlobalId()); |
| 566 | if (!dicts) { |
| 567 | dicts = CreateDictVector(); |
| 568 | // Since most lists will remain under 8 or 16 entries but will frequently |
| 569 | // be more than four, this prevents copying from 1->2->4->8. |
| 570 | dicts->reserve(8); |
| 571 | HashInsert(section_dict_, section_name, dicts); |
| 572 | } |
| 573 | assert(dicts != NULL); |
| 574 | const string newname(CreateSubdictName(name_, section_name, |
| 575 | dicts->size() + 1, "")); |
| 576 | TemplateDictionary* retval = CreateTemplateSubdict( |
| 577 | newname, arena_, this, template_global_dict_owner_); |
| 578 | dicts->push_back(retval); |
| 579 | return retval; |
| 580 | } |
| 581 | |
| 582 | |
| 583 | void TemplateDictionary::ShowSection(const TemplateString section_name) { |
| 584 | LazilyCreateDict(§ion_dict_); |
| 585 | if (!section_dict_->count(section_name.GetGlobalId())) { |
| 586 | TemplateDictionary* empty_dict = CreateTemplateSubdict( |
| 587 | "empty dictionary", arena_, this, template_global_dict_owner_); |
| 588 | DictVector* sub_dict = CreateDictVector(); |
| 589 | sub_dict->push_back(empty_dict); |
| 590 | HashInsert(section_dict_, section_name, sub_dict); |
| 591 | } |
| 592 | } |
| 593 | |
| 594 | void TemplateDictionary::ShowTemplateGlobalSection( |
| 595 | const TemplateString section_name) { |
| 596 | assert(template_global_dict_owner_ != NULL); |
| 597 | LazyCreateTemplateGlobalDict(); |
| 598 | template_global_dict_owner_->template_global_dict_-> |
| 599 | ShowSection(section_name); |
| 600 | } |
| 601 | |
| 602 | // ---------------------------------------------------------------------- |
| 603 | // TemplateDictionary::SetValueAndShowSection() |
| 604 | // TemplateDictionary::SetEscapedValueAndShowSection() |
| 605 | // If value is "", do nothing. Otherwise, call AddSectionDictionary() |
| 606 | // on the section and add exactly one entry to the sub-dictionary: |
| 607 | // the given variable/value pair. |
| 608 | // ---------------------------------------------------------------------- |
| 609 | |
| 610 | void TemplateDictionary::SetValueAndShowSection(const TemplateString variable, |
| 611 | const TemplateString value, |
| 612 | const TemplateString section_name) { |
| 613 | if (value.length_ == 0) // no value: the do-nothing case |
| 614 | return; |
| 615 | TemplateDictionary* sub_dict = AddSectionDictionary(section_name); |
| 616 | sub_dict->SetValue(variable, value); |
| 617 | } |
| 618 | |
| 619 | // ---------------------------------------------------------------------- |
| 620 | // TemplateDictionary::AddIncludeDictionary() |
| 621 | // This is much like AddSectionDictionary(). One major difference |
| 622 | // is that the new dictionary does not have a parent dictionary: |
| 623 | // there's no automatic variable inclusion across template-file |
| 624 | // boundaries. Note there is no ShowTemplate() -- you must always |
| 625 | // specify the dictionary to use explicitly. |
| 626 | // ---------------------------------------------------------------------- |
| 627 | |
| 628 | TemplateDictionary* TemplateDictionary::AddIncludeDictionary( |
| 629 | const TemplateString include_name) { |
| 630 | LazilyCreateDict(&include_dict_); |
| 631 | DictVector* dicts = find_ptr2(*include_dict_, include_name.GetGlobalId()); |
| 632 | if (!dicts) { |
| 633 | dicts = CreateDictVector(); |
| 634 | HashInsert(include_dict_, include_name, dicts); |
| 635 | } |
| 636 | assert(dicts != NULL); |
| 637 | const string newname(CreateSubdictName(name_, include_name, |
| 638 | dicts->size() + 1, "")); |
| 639 | TemplateDictionary* retval = CreateTemplateSubdict( |
| 640 | newname, arena_, NULL, template_global_dict_owner_); |
| 641 | dicts->push_back(retval); |
| 642 | return retval; |
| 643 | } |
| 644 | |
| 645 | |
| 646 | // ---------------------------------------------------------------------- |
| 647 | // TemplateDictionary::SetFilename() |
| 648 | // Sets the filename this dictionary is meant to be associated with. |
| 649 | // When set, it's possible to expand a template with just the |
| 650 | // template-dict; the template is loaded via SetFilename() (though |
| 651 | // we'd have to assume a value for strip). This is required for |
| 652 | // dictionaries that are meant to be used with an include-template. |
| 653 | // ---------------------------------------------------------------------- |
| 654 | |
| 655 | void TemplateDictionary::SetFilename(const TemplateString filename) { |
| 656 | filename_ = Memdup(filename).ptr_; |
| 657 | } |
| 658 | |
| 659 | // ---------------------------------------------------------------------- |
| 660 | // TemplateDictionary::AddToIdToNameMap() |
| 661 | // We have a problem when we try to dump the contents of the |
| 662 | // dictionary, because instead of storing the keys to global_dict_ |
| 663 | // etc as strings, we store them as integer id's. We need this |
| 664 | // map, from id->string, to be able to dump. This should be called |
| 665 | // every time we add a string to a TemplateDictionary hashtable. |
| 666 | // ---------------------------------------------------------------------- |
| 667 | |
| 668 | /*static*/ void TemplateDictionary::AddToIdToNameMap(TemplateId id, |
| 669 | const TemplateString& str) { |
| 670 | // If str.id_ is set, that means we were added to the id-to-name map |
| 671 | // at TemplateString constructor time, when the id_ was set. So we |
| 672 | // don't need to bother again here. |
| 673 | if (str.id_ != 0) { |
| 674 | return; |
| 675 | } |
| 676 | // Verify that if this id is already in the map, it's there with our |
| 677 | // contents. If not, that would mean a hash collision (since our |
| 678 | // id's are hash values). |
| 679 | DCHECK(TemplateString::IdToString(id) == kStsEmpty || |
| 680 | memcmp(str.ptr_, TemplateString::IdToString(id).ptr_, |
| 681 | str.length_) == 0) |
| 682 | << string(str.ptr_, str.length_) << " vs " |
| 683 | << string(TemplateString::IdToString(id).ptr_, |
| 684 | TemplateString::IdToString(id).length_); |
| 685 | TemplateString str_with_id(str.ptr_, str.length_, str.is_immutable(), id); |
| 686 | str_with_id.AddToGlobalIdToNameMap(); |
| 687 | } |
| 688 | |
| 689 | // ---------------------------------------------------------------------- |
| 690 | // TemplateDictionary::DumpToString() |
| 691 | // TemplateDictionary::Dump() |
| 692 | // The values are shown in the following order: |
| 693 | // - Scalar values |
| 694 | // - Sub-dictionaries and their associated section names. |
| 695 | // - Sub-dictionaries and their associated template names, with filename. |
| 696 | // ---------------------------------------------------------------------- |
| 697 | |
| 698 | // DictionaryPrinter knows how to dump a whole dictionary tree. |
| 699 | class TemplateDictionary::DictionaryPrinter { |
| 700 | public: |
| 701 | DictionaryPrinter(string* out, int initial_indent) |
| 702 | : writer_(out, initial_indent) { |
| 703 | } |
| 704 | |
| 705 | void DumpToString(const TemplateDictionary& dict) { |
| 706 | // Show globals if we're a top-level dictionary |
| 707 | if (dict.parent_dict_ == NULL) { |
| 708 | DumpGlobals(); |
| 709 | } |
| 710 | |
| 711 | // Show template-globals |
| 712 | if (dict.template_global_dict_ && !dict.template_global_dict_->Empty()) { |
| 713 | DumpTemplateGlobals(*dict.template_global_dict_); |
| 714 | } |
| 715 | |
| 716 | DumpDictionary(dict); |
| 717 | } |
| 718 | |
| 719 | private: |
| 720 | void FillSortedGlobalDictMap(map<string, string>* sorted_global_dict) |
| 721 | LOCKS_EXCLUDED(g_static_mutex) { |
| 722 | ReaderMutexLock ml(&g_static_mutex); |
| 723 | for (GlobalDict::const_iterator it = global_dict_->begin(); |
| 724 | it != global_dict_->end(); ++it) { |
| 725 | const TemplateString key = TemplateDictionary::IdToString(it->first); |
| 726 | assert(!InvalidTemplateString(key)); // checks key.ptr_ != NULL |
| 727 | (*sorted_global_dict)[PrintableTemplateString(key)] = |
| 728 | PrintableTemplateString(it->second); |
| 729 | } |
| 730 | } |
| 731 | void DumpGlobals() { |
| 732 | writer_.Write("global dictionary {\n"); |
| 733 | writer_.Indent(); |
| 734 | |
| 735 | // We could be faster than converting every TemplateString into a |
| 736 | // string and inserted into an ordered data structure, but why bother? |
| 737 | map<string, string> sorted_global_dict; |
| 738 | FillSortedGlobalDictMap(&sorted_global_dict); |
| 739 | for (map<string, string>::const_iterator it = sorted_global_dict.begin(); |
| 740 | it != sorted_global_dict.end(); ++it) { |
| 741 | writer_.Write(it->first + ": >" + it->second + "<\n"); |
| 742 | } |
| 743 | |
| 744 | writer_.Dedent(); |
| 745 | writer_.Write("};\n"); |
| 746 | } |
| 747 | |
| 748 | void DumpTemplateGlobals(const TemplateDictionary& template_global_dict) { |
| 749 | writer_.Write("template dictionary {\n"); |
| 750 | writer_.Indent(); |
| 751 | DumpDictionaryContent(template_global_dict); |
| 752 | writer_.Dedent(); |
| 753 | writer_.Write("};\n"); |
| 754 | } |
| 755 | |
| 756 | void DumpDictionary(const TemplateDictionary& dict) { |
| 757 | string intended_for = dict.filename_ && dict.filename_[0] ? |
| 758 | string(" (intended for ") + dict.filename_ + ")" : ""; |
| 759 | writer_.Write("dictionary '", PrintableTemplateString(dict.name_), |
| 760 | intended_for, "' {\n"); |
| 761 | writer_.Indent(); |
| 762 | DumpDictionaryContent(dict); |
| 763 | writer_.Dedent(); |
| 764 | writer_.Write("}\n"); |
| 765 | } |
| 766 | |
| 767 | void DumpDictionaryContent(const TemplateDictionary& dict) { |
| 768 | if (dict.variable_dict_) { // Show variables |
| 769 | DumpVariables(*dict.variable_dict_); |
| 770 | } |
| 771 | |
| 772 | |
| 773 | if (dict.section_dict_) { // Show section sub-dictionaries |
| 774 | DumpSectionDict(*dict.section_dict_); |
| 775 | } |
| 776 | |
| 777 | |
| 778 | if (dict.include_dict_) { // Show template-include sub-dictionaries |
| 779 | DumpIncludeDict(*dict.include_dict_); |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | void DumpVariables(const VariableDict& dict) { |
| 784 | map<string, string> sorted_variable_dict; |
| 785 | for (VariableDict::const_iterator it = dict.begin(); |
| 786 | it != dict.end(); ++it) { |
| 787 | const TemplateString key = TemplateDictionary::IdToString(it->first); |
| 788 | assert(!InvalidTemplateString(key)); // checks key.ptr_ != NULL |
| 789 | sorted_variable_dict[PrintableTemplateString(key)] = |
| 790 | PrintableTemplateString(it->second); |
| 791 | } |
| 792 | for (map<string,string>::const_iterator it = sorted_variable_dict.begin(); |
| 793 | it != sorted_variable_dict.end(); ++it) { |
| 794 | writer_.Write(it->first + ": >" + it->second + "<\n"); |
| 795 | } |
| 796 | } |
| 797 | |
| 798 | |
| 799 | template<typename MyMap, typename MySectionDict> |
| 800 | void SortSections(MyMap* sorted_section_dict, |
| 801 | const MySectionDict& section_dict) { |
| 802 | typename MySectionDict::const_iterator it = section_dict.begin(); |
| 803 | for (; it != section_dict.end(); ++it) { |
| 804 | const TemplateString key = TemplateDictionary::IdToString(it->first); |
| 805 | assert(!InvalidTemplateString(key)); // checks key.ptr_ != NULL |
| 806 | (*sorted_section_dict)[PrintableTemplateString(key)] = it->second; |
| 807 | } |
| 808 | } |
| 809 | |
| 810 | void DumpSectionDict(const SectionDict& section_dict) { |
| 811 | map<string, const DictVector*> sorted_section_dict; |
| 812 | SortSections(&sorted_section_dict, section_dict); |
| 813 | for (map<string, const DictVector*>::const_iterator it = |
| 814 | sorted_section_dict.begin(); |
| 815 | it != sorted_section_dict.end(); ++it) { |
| 816 | for (DictVector::const_iterator it2 = it->second->begin(); |
| 817 | it2 != it->second->end(); ++it2) { |
| 818 | TemplateDictionary* dict = *it2; |
| 819 | writer_.Write("section ", it->first, " (dict ", |
| 820 | GetDictNum(it2 - it->second->begin() + 1, |
| 821 | it->second->size()), |
| 822 | ") -->\n"); |
| 823 | writer_.Indent(); |
| 824 | DumpToString(*dict); |
| 825 | writer_.Dedent(); |
| 826 | } |
| 827 | } |
| 828 | } |
| 829 | |
| 830 | void DumpIncludeDict(const IncludeDict& include_dict) { |
| 831 | map<string, const DictVector*> sorted_include_dict; |
| 832 | SortSections(&sorted_include_dict, include_dict); |
| 833 | for (map<string, const DictVector*>::const_iterator it = |
| 834 | sorted_include_dict.begin(); |
| 835 | it != sorted_include_dict.end(); ++it) { |
| 836 | for (vector<TemplateDictionary*>::size_type i = 0; |
| 837 | i < it->second->size(); ++i) { |
| 838 | TemplateDictionary* dict = (*it->second)[i]; |
| 839 | string from_name = (dict->filename_ && *dict->filename_) ? |
| 840 | string(", from ") + dict->filename_ : |
| 841 | string(", **NO FILENAME SET; THIS DICT WILL BE IGNORED**"); |
| 842 | writer_.Write("include-template ", it->first, " (dict ", |
| 843 | GetDictNum(static_cast<int>(i + 1), it->second->size()), |
| 844 | from_name, ") -->\n"); |
| 845 | writer_.Indent(); |
| 846 | DumpToString(*dict); |
| 847 | writer_.Dedent(); |
| 848 | } |
| 849 | } |
| 850 | } |
| 851 | |
| 852 | string GetDictNum(size_t index, size_t size) const { |
| 853 | char buf[64]; // big enough for two ints |
| 854 | snprintf(buf, sizeof(buf), "%" PRIuS " of %" PRIuS, index, size); |
| 855 | return buf; |
| 856 | } |
| 857 | |
| 858 | IndentedWriter writer_; |
| 859 | }; |
| 860 | |
| 861 | void TemplateDictionary::DumpToString(string* out, int indent) const { |
| 862 | DictionaryPrinter printer(out, indent); |
| 863 | printer.DumpToString(*this); |
| 864 | } |
| 865 | |
| 866 | void TemplateDictionary::Dump(int indent) const { |
| 867 | string out; |
| 868 | DumpToString(&out, indent); |
| 869 | fwrite(out.data(), 1, out.length(), stdout); |
| 870 | fflush(stdout); |
| 871 | } |
| 872 | |
| 873 | // ---------------------------------------------------------------------- |
| 874 | // TemplateDictionary::Memdup() |
| 875 | // Copy the input into the arena, so we have a permanent copy of |
| 876 | // it. Returns a pointer to the arena-copy, as a TemplateString |
| 877 | // (in case the input has internal NULs). |
| 878 | // ---------------------------------------------------------------------- |
| 879 | |
| 880 | TemplateString TemplateDictionary::Memdup(const char* s, size_t slen) { |
| 881 | return TemplateString(arena_->MemdupPlusNUL(s, slen), slen); // add a \0 too |
| 882 | } |
| 883 | |
| 884 | |
| 885 | // ---------------------------------------------------------------------- |
| 886 | // TemplateDictionary::GetSectionValue() |
| 887 | // TemplateDictionary::IsHiddenSection() |
| 888 | // TemplateDictionary::IsHiddenTemplate() |
| 889 | // TemplateDictionary::GetIncludeTemplateName() |
| 890 | // The 'introspection' routines that tell Expand() what's in the |
| 891 | // template dictionary. GetSectionValue() does variable lookup: |
| 892 | // first look in this dict, then in parent dicts, etc. IsHidden*() |
| 893 | // returns true iff the name is not present in the appropriate |
| 894 | // dictionary. None of these functions ever returns NULL. |
| 895 | // ---------------------------------------------------------------------- |
| 896 | |
| 897 | TemplateString TemplateDictionary::GetValue( |
| 898 | const TemplateString& variable) const LOCKS_EXCLUDED(g_static_mutex) { |
| 899 | for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| 900 | if (d->variable_dict_) { |
| 901 | if (const TemplateString* it = find_ptr(*d->variable_dict_, variable.GetGlobalId())) |
| 902 | return *it; |
| 903 | } |
| 904 | } |
| 905 | |
| 906 | // No match in the dict tree. Check the template-global dict. |
| 907 | assert(template_global_dict_owner_ != NULL); |
| 908 | if (template_global_dict_owner_->template_global_dict_ |
| 909 | && template_global_dict_owner_->template_global_dict_->variable_dict_) { |
| 910 | const VariableDict* template_global_vars = |
| 911 | template_global_dict_owner_->template_global_dict_->variable_dict_; |
| 912 | |
| 913 | if (const TemplateString* it = find_ptr(*template_global_vars, variable.GetGlobalId())) |
| 914 | return *it; |
| 915 | } |
| 916 | |
| 917 | // No match in dict tree or template-global dict. Last chance: global dict. |
| 918 | { |
| 919 | ReaderMutexLock ml(&g_static_mutex); |
| 920 | if (const TemplateString* it = find_ptr(*global_dict_, variable.GetGlobalId())) |
| 921 | return *it; |
| 922 | return *empty_string_; |
| 923 | } |
| 924 | } |
| 925 | |
| 926 | bool TemplateDictionary::IsHiddenSection(const TemplateString& name) const { |
| 927 | for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| 928 | if (d->section_dict_ && |
| 929 | d->section_dict_->count(name.GetGlobalId())) |
| 930 | return false; |
| 931 | } |
| 932 | assert(template_global_dict_owner_ != NULL); |
| 933 | if (template_global_dict_owner_->template_global_dict_ && |
| 934 | template_global_dict_owner_->template_global_dict_->section_dict_) { |
| 935 | SectionDict* sections = |
| 936 | template_global_dict_owner_->template_global_dict_->section_dict_; |
| 937 | if (sections->count(name.GetGlobalId())) { |
| 938 | return false; |
| 939 | } |
| 940 | } |
| 941 | return true; |
| 942 | } |
| 943 | |
| 944 | bool TemplateDictionary::IsHiddenTemplate(const TemplateString& name) const { |
| 945 | for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| 946 | if (d->include_dict_ && |
| 947 | d->include_dict_->count(name.GetGlobalId())) |
| 948 | return false; |
| 949 | } |
| 950 | return true; |
| 951 | } |
| 952 | |
| 953 | const char *TemplateDictionary::GetIncludeTemplateName( |
| 954 | const TemplateString& variable, int dictnum) const { |
| 955 | for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| 956 | if (d->include_dict_) { |
| 957 | if (DictVector* it = find_ptr2(*d->include_dict_, variable.GetGlobalId())) { |
| 958 | TemplateDictionary* dict = (*it)[dictnum]; |
| 959 | return dict->filename_ ? dict->filename_ : ""; // map NULL to "" |
| 960 | } |
| 961 | } |
| 962 | } |
| 963 | assert("Call IsHiddenTemplate before GetIncludeTemplateName" && 0); |
| 964 | abort(); |
| 965 | } |
| 966 | |
| 967 | bool TemplateDictionary::Empty() const { |
| 968 | if ((variable_dict_ && !variable_dict_->empty()) || |
| 969 | (section_dict_ && section_dict_->empty()) || |
| 970 | (include_dict_ && include_dict_->empty())) { |
| 971 | return false; |
| 972 | } |
| 973 | return true; |
| 974 | } |
| 975 | |
| 976 | // ---------------------------------------------------------------------- |
| 977 | // TemplateDictionary::CreateSectionIterator() |
| 978 | // TemplateDictionary::CreateTemplateIterator() |
| 979 | // TemplateDictionary::Iterator::HasNext() |
| 980 | // TemplateDictionary::Iterator::Next() |
| 981 | // Iterator framework. |
| 982 | // ---------------------------------------------------------------------- |
| 983 | |
| 984 | template <typename T> bool TemplateDictionary::Iterator<T>::HasNext() const { |
| 985 | return begin_ != end_; |
| 986 | } |
| 987 | |
| 988 | template <typename T> const TemplateDictionaryInterface& |
| 989 | TemplateDictionary::Iterator<T>::Next() { |
| 990 | return **(begin_++); |
| 991 | } |
| 992 | |
| 993 | TemplateDictionaryInterface::Iterator* |
| 994 | TemplateDictionary::CreateTemplateIterator( |
| 995 | const TemplateString& section_name) const { |
| 996 | for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| 997 | if (d->include_dict_) { |
| 998 | if (DictVector* it = find_ptr2(*d->include_dict_, section_name.GetGlobalId())) { |
| 999 | // Found it! Return it as an Iterator |
| 1000 | return MakeIterator(*it); |
| 1001 | } |
| 1002 | } |
| 1003 | } |
| 1004 | assert("Call IsHiddenTemplate before CreateTemplateIterator" && 0); |
| 1005 | abort(); |
| 1006 | } |
| 1007 | |
| 1008 | TemplateDictionaryInterface::Iterator* |
| 1009 | TemplateDictionary::CreateSectionIterator( |
| 1010 | const TemplateString& section_name) const { |
| 1011 | for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| 1012 | if (d->section_dict_) { |
| 1013 | if (const DictVector* it = find_ptr2(*d->section_dict_, section_name.GetGlobalId())) { |
| 1014 | // Found it! Return it as an Iterator |
| 1015 | return MakeIterator(*it); |
| 1016 | } |
| 1017 | } |
| 1018 | } |
| 1019 | // Check the template global dictionary. |
| 1020 | assert(template_global_dict_owner_); |
| 1021 | const TemplateDictionary* template_global_dict = |
| 1022 | template_global_dict_owner_->template_global_dict_; |
| 1023 | if (template_global_dict && template_global_dict->section_dict_) { |
| 1024 | if (const DictVector* it = find_ptr2(*template_global_dict->section_dict_, section_name.GetGlobalId())) { |
| 1025 | return MakeIterator(*it); |
| 1026 | } |
| 1027 | } |
| 1028 | assert("Call IsHiddenSection before GetDictionaries" && 0); |
| 1029 | abort(); |
| 1030 | } |
| 1031 | |
| 1032 | } |