| // Copyright (c) 2006, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // --- |
| // Author: csilvers@google.com (Craig Silverstein) |
| // |
| // Based on the 'old' TemplateDictionary by Frank Jernigan. |
| |
| #include <config.h> |
| #include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <stdarg.h> |
| #include <algorithm> // for sort() |
| #include HASH_MAP_H |
| #include <map> |
| #include <string> |
| #include <utility> // for pair<> |
| #include <vector> |
| |
| #include "base/arena-inl.h" |
| #include "base/thread_annotations.h" |
| #include "indented_writer.h" |
| #include <ctemplate/find_ptr.h> |
| #include <ctemplate/template_dictionary.h> |
| #include <ctemplate/template_modifiers.h> |
| #include "base/small_map.h" |
| #include "base/util.h" // for DCHECK |
| |
| using std::vector; |
| using std::string; |
| using std::map; |
| using std::pair; |
| using std::make_pair; |
| |
| namespace ctemplate { |
| |
| // Guards the initialization of the global dictionary. |
| static GoogleOnceType g_once = GOOGLE_ONCE_INIT; |
| // Guard access to the global dictionary. |
| static Mutex g_static_mutex(base::LINKER_INITIALIZED); |
| |
| /*static*/ UnsafeArena* const TemplateDictionary::NO_ARENA = NULL; |
| /*static*/ TemplateDictionary::GlobalDict* TemplateDictionary::global_dict_ |
| GUARDED_BY(g_static_mutex) PT_GUARDED_BY(g_static_mutex) = NULL; |
| /*static*/ TemplateString* TemplateDictionary::empty_string_ = NULL; |
| |
| |
| static const char* const kAnnotateOutput = "__ctemplate_annotate_output__"; |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::map_arena_init |
| // This class is what small_map<> uses to create a new |
| // arena-allocated map<> when it decides it needs to do that. |
| // ---------------------------------------------------------------------- |
| |
| class TemplateDictionary::map_arena_init { |
| public: |
| map_arena_init(UnsafeArena* arena) : arena_(arena) { } |
| template<typename T> void operator ()(ManualConstructor<T>* map) const { |
| map->Init(typename T::key_compare(), arena_); |
| } |
| private: |
| UnsafeArena* arena_; |
| }; |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::LazilyCreateDict() |
| // TemplateDictionary::LazilyCreateTemplateGlobalDict() |
| // TemplateDictionary::CreateDictVector() |
| // TemplateDictionary::CreateTemplateSubdict() |
| // These routines allocate the objects that TemplateDictionary |
| // allocates (sub-dictionaries, variable maps, etc). Each |
| // allocates memory on the arena, and instructs the STL objects |
| // to use the arena for their own internal allocations as well. |
| // ---------------------------------------------------------------------- |
| |
| template<typename T> |
| inline void TemplateDictionary::LazilyCreateDict(T** dict) { |
| if (*dict != NULL) |
| return; |
| // Placement new: construct the map in the memory used by *dict. |
| void* buffer = arena_->AllocAligned(sizeof(**dict), |
| BaseArena::kDefaultAlignment); |
| new (buffer) T(arena_); |
| *dict = reinterpret_cast<T*>(buffer); |
| } |
| |
| inline void TemplateDictionary::LazyCreateTemplateGlobalDict() { |
| if (!template_global_dict_owner_->template_global_dict_) { |
| template_global_dict_owner_->template_global_dict_ = |
| CreateTemplateSubdict("Template Globals", arena_, |
| template_global_dict_owner_, |
| template_global_dict_owner_); |
| } |
| } |
| |
| inline TemplateDictionary::DictVector* TemplateDictionary::CreateDictVector() { |
| void* buffer = arena_->AllocAligned(sizeof(DictVector), |
| BaseArena::kDefaultAlignment); |
| // Placement new: construct the vector in the memory used by buffer. |
| new (buffer) DictVector(arena_); |
| return reinterpret_cast<DictVector*>(buffer); |
| } |
| |
| inline TemplateDictionary* TemplateDictionary::CreateTemplateSubdict( |
| const TemplateString& name, |
| UnsafeArena* arena, |
| TemplateDictionary* parent_dict, |
| TemplateDictionary* template_global_dict_owner) { |
| void* buffer = arena->AllocAligned(sizeof(TemplateDictionary), |
| BaseArena::kDefaultAlignment); |
| // Placement new: construct the sub-tpl in the memory used by tplbuf. |
| new (buffer) TemplateDictionary(name, arena, parent_dict, |
| template_global_dict_owner); |
| return reinterpret_cast<TemplateDictionary*>(buffer); |
| } |
| |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::HashInsert() |
| // A convenience function that's equivalent to m[key] = value, but |
| // converting the key to an id first, and without necessarily needing |
| // key to have a default constructor like operator[] does. It also |
| // inserts (key, id(key)) into a map to allow for id->key mapping. |
| // ---------------------------------------------------------------------- |
| |
| // By default, prefer the m[key] = value construct. We do something |
| // more complex for TemplateString, though, since m[key] requires a |
| // zero-arg constructor, which TemplateString doesn't have. We could |
| // do the more complex thing everywhere, but that seems to trigger a |
| // bug in in gcc 4.1.2 (at least) when compiled with -O2. Shrug. |
| namespace { |
| template<typename MapType, typename ValueType> |
| inline void DoHashInsert(MapType* m, TemplateId id, ValueType value) { |
| (*m)[id] = value; |
| } |
| |
| template<typename MapType> |
| inline void DoHashInsert(MapType* m, TemplateId id, TemplateString value) { |
| pair<typename MapType::iterator, bool> r |
| = m->insert(typename MapType::value_type(id, value)); |
| // Unfortunately, insert() doesn't actually replace if key is |
| // already in the map. Thus, in that case (insert().second == false), |
| // we need to overwrite the old value. Since TemplateString |
| // doesn't define operator=, the easiest legal way to overwrite is |
| // to use the copy-constructor with placement-new. Note that since |
| // TemplateString has no destructor, we don't need to call the |
| // destructor to 'clear out' the old value. |
| if (r.second == false) { |
| new (&r.first->second) TemplateString(value); |
| } |
| } |
| } |
| |
| template<typename MapType, typename ValueType> |
| void TemplateDictionary::HashInsert(MapType* m, |
| TemplateString key, ValueType value) { |
| const TemplateId id = key.GetGlobalId(); |
| DoHashInsert(m, id, value); |
| AddToIdToNameMap(id, key); // allows us to do the hash-key -> name mapping |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::SetupGlobalDict() |
| // Must be called exactly once before accessing global_dict_. |
| // GoogleOnceInit() is used to manage that initialization in a thread-safe |
| // way. |
| // ---------------------------------------------------------------------- |
| /*static*/ void TemplateDictionary::SetupGlobalDict() |
| NO_THREAD_SAFETY_ANALYSIS { |
| global_dict_ = new TemplateDictionary::GlobalDict; |
| // Initialize the built-ins |
| HashInsert(global_dict_, TemplateString("BI_SPACE"), TemplateString(" ")); |
| HashInsert(global_dict_, TemplateString("BI_NEWLINE"), TemplateString("\n")); |
| // This is used for name-lookup misses. |
| empty_string_ = new TemplateString(""); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::TemplateDictionary() |
| // TemplateDictionary::~TemplateDictionary() |
| // The only tricky thing is that we make sure the static vars are |
| // set up properly. This must be done at each construct time, |
| // because it's the responsibility of the first dictionary created |
| // in the program to set up the globals, and that could be us. |
| // The UnsafeArena() argument is how big to make each arena |
| // block. Too big and space is wasted. Too small and we spend |
| // a lot of time allocating new arena blocks. 32k seems right. |
| // ---------------------------------------------------------------------- |
| |
| TemplateDictionary::TemplateDictionary(const TemplateString& name, |
| UnsafeArena* arena) |
| : arena_(arena ? arena : new UnsafeArena(32768)), |
| should_delete_arena_(arena ? false : true), // true if we called new |
| name_(Memdup(name)), // arena must have been set up first |
| variable_dict_(NULL), |
| section_dict_(NULL), |
| include_dict_(NULL), |
| template_global_dict_(NULL), |
| template_global_dict_owner_(this), |
| parent_dict_(NULL), |
| filename_(NULL) { |
| GoogleOnceInit(&g_once, &SetupGlobalDict); |
| } |
| |
| TemplateDictionary::TemplateDictionary( |
| const TemplateString& name, |
| UnsafeArena* arena, |
| TemplateDictionary* parent_dict, |
| TemplateDictionary* template_global_dict_owner) |
| : arena_(arena), should_delete_arena_(false), // parents own it |
| name_(Memdup(name)), // arena must have been set up first |
| variable_dict_(NULL), |
| section_dict_(NULL), |
| include_dict_(NULL), |
| template_global_dict_(NULL), |
| template_global_dict_owner_(template_global_dict_owner), |
| parent_dict_(parent_dict), |
| filename_(NULL) { |
| assert(template_global_dict_owner_ != NULL); |
| GoogleOnceInit(&g_once, &SetupGlobalDict); |
| } |
| |
| TemplateDictionary::~TemplateDictionary() { |
| // Everything we allocate, we allocate on the arena, so we |
| // don't need to free anything here. |
| if (should_delete_arena_) { |
| delete arena_; |
| } |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::MakeCopy() |
| // Makes a recursive copy: so we copy any include dictionaries and |
| // section dictionaries we see as well. InternalMakeCopy() is |
| // needed just so we can ensure that if we're doing a copy of a |
| // subtree, it's due to a recursive call. Returns NULL if there |
| // is an error copying. |
| // ---------------------------------------------------------------------- |
| |
| TemplateDictionary* TemplateDictionary::InternalMakeCopy( |
| const TemplateString& name_of_copy, |
| UnsafeArena* arena, |
| TemplateDictionary* parent_dict, |
| TemplateDictionary* template_global_dict_owner) { |
| |
| TemplateDictionary* newdict; |
| if (template_global_dict_owner_ == this) { |
| // We're a root-level template. We want the copy to be just like |
| // us, and have its own template_global_dict_, that it owns. |
| // We use the normal global new, since newdict will be returned |
| // to the user. |
| newdict = new TemplateDictionary(name_of_copy, arena); |
| } else { // recursive calls use private contructor |
| // We're not a root-level template, so we want the copy to refer to the |
| // same template_global_dict_ owner that we do. |
| // Note: we always use our own arena, even when we have a parent |
| // (though we have the same arena as our parent when we have one). |
| assert(arena); |
| assert(parent_dict ? arena == parent_dict->arena_ : true); |
| newdict = CreateTemplateSubdict(name_of_copy, arena, |
| parent_dict, template_global_dict_owner); |
| } |
| |
| // Copy the variable dictionary |
| if (variable_dict_) { |
| newdict->LazilyCreateDict(&newdict->variable_dict_); |
| for (VariableDict::const_iterator it = variable_dict_->begin(); |
| it != variable_dict_->end(); ++it) { |
| newdict->variable_dict_->insert(make_pair(it->first, |
| newdict->Memdup(it->second))); |
| } |
| } |
| // ...and the template-global-dict, if we have one (only root-level tpls do) |
| if (template_global_dict_) { |
| newdict->template_global_dict_ = template_global_dict_->InternalMakeCopy( |
| template_global_dict_->name(), newdict->arena_, newdict, |
| newdict->template_global_dict_owner_); |
| } |
| // Copy the section dictionary |
| if (section_dict_) { |
| newdict->LazilyCreateDict(&newdict->section_dict_); |
| for (SectionDict::iterator it = section_dict_->begin(); |
| it != section_dict_->end(); ++it) { |
| DictVector* dicts = newdict->CreateDictVector(); |
| newdict->section_dict_->insert(make_pair(it->first, dicts)); |
| for (DictVector::iterator it2 = it->second->begin(); |
| it2 != it->second->end(); ++it2) { |
| TemplateDictionary* subdict = *it2; |
| // In this case, we pass in newdict as the parent of our new dict. |
| dicts->push_back(subdict->InternalMakeCopy( |
| subdict->name(), newdict->arena_, |
| newdict, newdict->template_global_dict_owner_)); |
| } |
| } |
| } |
| // Copy the includes-dictionary |
| if (include_dict_) { |
| newdict->LazilyCreateDict(&newdict->include_dict_); |
| for (IncludeDict::iterator it = include_dict_->begin(); |
| it != include_dict_->end(); ++it) { |
| DictVector* dicts = newdict->CreateDictVector(); |
| newdict->include_dict_->insert(make_pair(it->first, dicts)); |
| for (DictVector::iterator it2 = it->second->begin(); |
| it2 != it->second->end(); ++it2) { |
| TemplateDictionary* subdict = *it2; |
| // In this case, we pass in NULL as the parent of our new dict: |
| // parents are not inherited across include-dictionaries. |
| dicts->push_back(subdict->InternalMakeCopy( |
| subdict->name(), newdict->arena_, |
| NULL, newdict->template_global_dict_owner_)); |
| } |
| } |
| } |
| |
| // Finally, copy everything else not set properly by the constructor |
| newdict->filename_ = newdict->Memdup(filename_).ptr_; |
| |
| return newdict; |
| } |
| |
| TemplateDictionary* TemplateDictionary::MakeCopy( |
| const TemplateString& name_of_copy, UnsafeArena* arena) { |
| if (template_global_dict_owner_ != this) { |
| // We're not at the root, which is illegal. |
| return NULL; |
| } |
| return InternalMakeCopy(name_of_copy, arena, |
| NULL, template_global_dict_owner_); |
| } |
| |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::StringAppendV() |
| // Does an snprintf to a string. Idea is to grow string as needed. |
| // Writes to space if possible -- caller must ensure space has |
| // size at least 1024 -- and if not allocates a buffer of its |
| // own which the caller must free. Sets out to the buffer written |
| // to (space or something else). Returns the number of bytes |
| // written into out. |
| // ---------------------------------------------------------------------- |
| |
| int TemplateDictionary::StringAppendV(char* space, char** out, |
| const char* format, va_list ap) { |
| const int kBufsize = 1024; |
| // It's possible for methods that use a va_list to invalidate |
| // the data in it upon use. The fix is to make a copy |
| // of the structure before using it and use that copy instead. |
| va_list backup_ap; |
| va_copy(backup_ap, ap); |
| int result = vsnprintf(space, kBufsize, format, backup_ap); |
| va_end(backup_ap); |
| |
| if ((result >= 0) && (result < kBufsize)) { |
| *out = space; |
| return result; // It fit |
| } |
| |
| // Repeatedly increase buffer size until it fits |
| int length = kBufsize; |
| while (true) { |
| if (result < 0) { |
| // Older snprintf() behavior. :-( Just try doubling the buffer size |
| length *= 2; |
| } else { |
| // We need exactly "result+1" characters |
| length = result+1; |
| } |
| char* buf = new char[length]; |
| |
| // Restore the va_list before we use it again |
| va_copy(backup_ap, ap); |
| result = vsnprintf(buf, length, format, backup_ap); |
| va_end(backup_ap); |
| |
| if ((result >= 0) && (result < length)) { |
| *out = buf; |
| return result; |
| } |
| delete[] buf; |
| } |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::SetValue() |
| // TemplateDictionary::SetIntValue() |
| // TemplateDictionary::SetFormattedValue() |
| // TemplateDictionary::SetEscapedValue() |
| // TemplateDictionary::SetEscapedFormattedValue() |
| // The functions to set the value of a variable. For each, |
| // I first define the char*+length version. Then, after those |
| // five definitions, I define a zillion alternate versions: |
| // strings, char*s, etc. The only non-obvious thing about |
| // each function is I make sure to copy both key and value to |
| // the arena, so we have our own, persistent copy of them. |
| // ---------------------------------------------------------------------- |
| |
| void TemplateDictionary::SetValue(const TemplateString variable, |
| const TemplateString value) { |
| LazilyCreateDict(&variable_dict_); |
| HashInsert(variable_dict_, variable, Memdup(value)); |
| } |
| |
| void TemplateDictionary::SetValueWithoutCopy(const TemplateString variable, |
| const TemplateString value) { |
| LazilyCreateDict(&variable_dict_); |
| // Don't memdup value - the caller will manage memory. |
| HashInsert(variable_dict_, variable, value); |
| } |
| |
| void TemplateDictionary::SetIntValue(const TemplateString variable, |
| long value) { |
| char buffer[64]; // big enough for any int |
| int valuelen = snprintf(buffer, sizeof(buffer), "%ld", value); |
| LazilyCreateDict(&variable_dict_); |
| HashInsert(variable_dict_, variable, Memdup(buffer, valuelen)); |
| } |
| |
| void TemplateDictionary::SetFormattedValue(const TemplateString variable, |
| const char* format, ...) { |
| char* buffer; |
| |
| char* scratch = arena_->Alloc(1024); // StringAppendV requires >=1024 bytes |
| va_list ap; |
| va_start(ap, format); |
| const int buflen = StringAppendV(scratch, &buffer, format, ap); |
| va_end(ap); |
| |
| LazilyCreateDict(&variable_dict_); |
| |
| // If it fit into scratch, great, otherwise we need to copy into arena |
| if (buffer == scratch) { |
| scratch = arena_->Shrink(scratch, buflen+1); // from 1024 to |value+\0| |
| HashInsert(variable_dict_, variable, TemplateString(scratch, buflen)); |
| } else { |
| arena_->Shrink(scratch, 0); // reclaim arena space we didn't use |
| HashInsert(variable_dict_, variable, Memdup(buffer, buflen)); |
| delete[] buffer; |
| } |
| } |
| |
| void TemplateDictionary::SetEscapedValue(TemplateString variable, |
| TemplateString value, |
| const TemplateModifier& escfn) { |
| SetValue(variable, string(escfn(value.data(), value.size()))); |
| } |
| |
| void TemplateDictionary::SetEscapedFormattedValue(TemplateString variable, |
| const TemplateModifier& escfn, |
| const char* format, ...) { |
| char* buffer; |
| |
| char* scratch = arena_->Alloc(1024); // StringAppendV requires >=1024 bytes |
| va_list ap; |
| va_start(ap, format); |
| const int buflen = StringAppendV(scratch, &buffer, format, ap); |
| va_end(ap); |
| |
| string escaped_string(escfn(buffer, buflen)); |
| // Reclaim the arena space: the value we care about is now in escaped_string |
| arena_->Shrink(scratch, 0); // reclaim arena space we didn't use |
| if (buffer != scratch) |
| delete[] buffer; |
| |
| SetValue(variable, escaped_string); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::SetTemplateGlobalValue() |
| // Sets a value in the template-global dict. Unlike normal |
| // variable lookups, these persist across sub-includes. |
| // ---------------------------------------------------------------------- |
| |
| void TemplateDictionary::SetTemplateGlobalValue(const TemplateString variable, |
| const TemplateString value) { |
| assert(template_global_dict_owner_ != NULL); |
| LazyCreateTemplateGlobalDict(); |
| template_global_dict_owner_->template_global_dict_->SetValue(variable, value); |
| } |
| |
| void TemplateDictionary::SetTemplateGlobalValueWithoutCopy( |
| const TemplateString variable, |
| const TemplateString value) { |
| assert(template_global_dict_owner_ != NULL); |
| LazyCreateTemplateGlobalDict(); |
| // Don't memdup value - the caller will manage memory. |
| template_global_dict_owner_->template_global_dict_-> |
| SetValueWithoutCopy(variable, value); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::SetGlobalValue() |
| // Sets a value in the global dict. Note this is a static method. |
| // ---------------------------------------------------------------------- |
| |
| /*static*/ void TemplateDictionary::SetGlobalValue( |
| const TemplateString variable, |
| const TemplateString value) LOCKS_EXCLUDED(g_static_mutex) { |
| // We can't use memdup here, since we're a static method. We do a strdup, |
| // which is fine, since global_dict_ lives the entire program anyway. |
| // It's unnecessary to copy the variable, since HashInsert takes care of |
| // that for us. |
| char* value_copy = new char[value.length_ + 1]; |
| memcpy(value_copy, value.ptr_, value.length_); |
| value_copy[value.length_] = '\0'; |
| |
| GoogleOnceInit(&g_once, &SetupGlobalDict); |
| |
| MutexLock ml(&g_static_mutex); |
| HashInsert(global_dict_, |
| variable, |
| TemplateString(value_copy, value.length_)); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::AddSectionDictionary() |
| // TemplateDictionary::ShowSection() |
| // TemplateDictionary::ShowTemplateGlobalSection() |
| // The new dictionary starts out empty, with us as the parent. |
| // It shares our arena. The name is constructed out of our |
| // name plus the section name. ShowSection() is the equivalent |
| // to AddSectionDictionary("empty_dict"). |
| // ---------------------------------------------------------------------- |
| |
| /*static*/ string TemplateDictionary::CreateSubdictName( |
| const TemplateString& dict_name, const TemplateString& sub_name, |
| size_t index, const char* suffix) { |
| char index_str[64]; |
| snprintf(index_str, sizeof(index_str), "%" PRIuS, index); |
| return (PrintableTemplateString(dict_name) + "/" + |
| PrintableTemplateString(sub_name) + "#" + index_str + suffix); |
| } |
| |
| TemplateDictionary* TemplateDictionary::AddSectionDictionary( |
| const TemplateString section_name) { |
| LazilyCreateDict(§ion_dict_); |
| DictVector* dicts = find_ptr2(*section_dict_, section_name.GetGlobalId()); |
| if (!dicts) { |
| dicts = CreateDictVector(); |
| // Since most lists will remain under 8 or 16 entries but will frequently |
| // be more than four, this prevents copying from 1->2->4->8. |
| dicts->reserve(8); |
| HashInsert(section_dict_, section_name, dicts); |
| } |
| assert(dicts != NULL); |
| const string newname(CreateSubdictName(name_, section_name, |
| dicts->size() + 1, "")); |
| TemplateDictionary* retval = CreateTemplateSubdict( |
| newname, arena_, this, template_global_dict_owner_); |
| dicts->push_back(retval); |
| return retval; |
| } |
| |
| |
| void TemplateDictionary::ShowSection(const TemplateString section_name) { |
| LazilyCreateDict(§ion_dict_); |
| if (!section_dict_->count(section_name.GetGlobalId())) { |
| TemplateDictionary* empty_dict = CreateTemplateSubdict( |
| "empty dictionary", arena_, this, template_global_dict_owner_); |
| DictVector* sub_dict = CreateDictVector(); |
| sub_dict->push_back(empty_dict); |
| HashInsert(section_dict_, section_name, sub_dict); |
| } |
| } |
| |
| void TemplateDictionary::ShowTemplateGlobalSection( |
| const TemplateString section_name) { |
| assert(template_global_dict_owner_ != NULL); |
| LazyCreateTemplateGlobalDict(); |
| template_global_dict_owner_->template_global_dict_-> |
| ShowSection(section_name); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::SetValueAndShowSection() |
| // TemplateDictionary::SetEscapedValueAndShowSection() |
| // If value is "", do nothing. Otherwise, call AddSectionDictionary() |
| // on the section and add exactly one entry to the sub-dictionary: |
| // the given variable/value pair. |
| // ---------------------------------------------------------------------- |
| |
| void TemplateDictionary::SetValueAndShowSection(const TemplateString variable, |
| const TemplateString value, |
| const TemplateString section_name) { |
| if (value.length_ == 0) // no value: the do-nothing case |
| return; |
| TemplateDictionary* sub_dict = AddSectionDictionary(section_name); |
| sub_dict->SetValue(variable, value); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::AddIncludeDictionary() |
| // This is much like AddSectionDictionary(). One major difference |
| // is that the new dictionary does not have a parent dictionary: |
| // there's no automatic variable inclusion across template-file |
| // boundaries. Note there is no ShowTemplate() -- you must always |
| // specify the dictionary to use explicitly. |
| // ---------------------------------------------------------------------- |
| |
| TemplateDictionary* TemplateDictionary::AddIncludeDictionary( |
| const TemplateString include_name) { |
| LazilyCreateDict(&include_dict_); |
| DictVector* dicts = find_ptr2(*include_dict_, include_name.GetGlobalId()); |
| if (!dicts) { |
| dicts = CreateDictVector(); |
| HashInsert(include_dict_, include_name, dicts); |
| } |
| assert(dicts != NULL); |
| const string newname(CreateSubdictName(name_, include_name, |
| dicts->size() + 1, "")); |
| TemplateDictionary* retval = CreateTemplateSubdict( |
| newname, arena_, NULL, template_global_dict_owner_); |
| dicts->push_back(retval); |
| return retval; |
| } |
| |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::SetFilename() |
| // Sets the filename this dictionary is meant to be associated with. |
| // When set, it's possible to expand a template with just the |
| // template-dict; the template is loaded via SetFilename() (though |
| // we'd have to assume a value for strip). This is required for |
| // dictionaries that are meant to be used with an include-template. |
| // ---------------------------------------------------------------------- |
| |
| void TemplateDictionary::SetFilename(const TemplateString filename) { |
| filename_ = Memdup(filename).ptr_; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::AddToIdToNameMap() |
| // We have a problem when we try to dump the contents of the |
| // dictionary, because instead of storing the keys to global_dict_ |
| // etc as strings, we store them as integer id's. We need this |
| // map, from id->string, to be able to dump. This should be called |
| // every time we add a string to a TemplateDictionary hashtable. |
| // ---------------------------------------------------------------------- |
| |
| /*static*/ void TemplateDictionary::AddToIdToNameMap(TemplateId id, |
| const TemplateString& str) { |
| // If str.id_ is set, that means we were added to the id-to-name map |
| // at TemplateString constructor time, when the id_ was set. So we |
| // don't need to bother again here. |
| if (str.id_ != 0) { |
| return; |
| } |
| // Verify that if this id is already in the map, it's there with our |
| // contents. If not, that would mean a hash collision (since our |
| // id's are hash values). |
| DCHECK(TemplateString::IdToString(id) == kStsEmpty || |
| memcmp(str.ptr_, TemplateString::IdToString(id).ptr_, |
| str.length_) == 0) |
| << string(str.ptr_, str.length_) << " vs " |
| << string(TemplateString::IdToString(id).ptr_, |
| TemplateString::IdToString(id).length_); |
| TemplateString str_with_id(str.ptr_, str.length_, str.is_immutable(), id); |
| str_with_id.AddToGlobalIdToNameMap(); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::DumpToString() |
| // TemplateDictionary::Dump() |
| // The values are shown in the following order: |
| // - Scalar values |
| // - Sub-dictionaries and their associated section names. |
| // - Sub-dictionaries and their associated template names, with filename. |
| // ---------------------------------------------------------------------- |
| |
| // DictionaryPrinter knows how to dump a whole dictionary tree. |
| class TemplateDictionary::DictionaryPrinter { |
| public: |
| DictionaryPrinter(string* out, int initial_indent) |
| : writer_(out, initial_indent) { |
| } |
| |
| void DumpToString(const TemplateDictionary& dict) { |
| // Show globals if we're a top-level dictionary |
| if (dict.parent_dict_ == NULL) { |
| DumpGlobals(); |
| } |
| |
| // Show template-globals |
| if (dict.template_global_dict_ && !dict.template_global_dict_->Empty()) { |
| DumpTemplateGlobals(*dict.template_global_dict_); |
| } |
| |
| DumpDictionary(dict); |
| } |
| |
| private: |
| void FillSortedGlobalDictMap(map<string, string>* sorted_global_dict) |
| LOCKS_EXCLUDED(g_static_mutex) { |
| ReaderMutexLock ml(&g_static_mutex); |
| for (GlobalDict::const_iterator it = global_dict_->begin(); |
| it != global_dict_->end(); ++it) { |
| const TemplateString key = TemplateDictionary::IdToString(it->first); |
| assert(!InvalidTemplateString(key)); // checks key.ptr_ != NULL |
| (*sorted_global_dict)[PrintableTemplateString(key)] = |
| PrintableTemplateString(it->second); |
| } |
| } |
| void DumpGlobals() { |
| writer_.Write("global dictionary {\n"); |
| writer_.Indent(); |
| |
| // We could be faster than converting every TemplateString into a |
| // string and inserted into an ordered data structure, but why bother? |
| map<string, string> sorted_global_dict; |
| FillSortedGlobalDictMap(&sorted_global_dict); |
| for (map<string, string>::const_iterator it = sorted_global_dict.begin(); |
| it != sorted_global_dict.end(); ++it) { |
| writer_.Write(it->first + ": >" + it->second + "<\n"); |
| } |
| |
| writer_.Dedent(); |
| writer_.Write("};\n"); |
| } |
| |
| void DumpTemplateGlobals(const TemplateDictionary& template_global_dict) { |
| writer_.Write("template dictionary {\n"); |
| writer_.Indent(); |
| DumpDictionaryContent(template_global_dict); |
| writer_.Dedent(); |
| writer_.Write("};\n"); |
| } |
| |
| void DumpDictionary(const TemplateDictionary& dict) { |
| string intended_for = dict.filename_ && dict.filename_[0] ? |
| string(" (intended for ") + dict.filename_ + ")" : ""; |
| writer_.Write("dictionary '", PrintableTemplateString(dict.name_), |
| intended_for, "' {\n"); |
| writer_.Indent(); |
| DumpDictionaryContent(dict); |
| writer_.Dedent(); |
| writer_.Write("}\n"); |
| } |
| |
| void DumpDictionaryContent(const TemplateDictionary& dict) { |
| if (dict.variable_dict_) { // Show variables |
| DumpVariables(*dict.variable_dict_); |
| } |
| |
| |
| if (dict.section_dict_) { // Show section sub-dictionaries |
| DumpSectionDict(*dict.section_dict_); |
| } |
| |
| |
| if (dict.include_dict_) { // Show template-include sub-dictionaries |
| DumpIncludeDict(*dict.include_dict_); |
| } |
| } |
| |
| void DumpVariables(const VariableDict& dict) { |
| map<string, string> sorted_variable_dict; |
| for (VariableDict::const_iterator it = dict.begin(); |
| it != dict.end(); ++it) { |
| const TemplateString key = TemplateDictionary::IdToString(it->first); |
| assert(!InvalidTemplateString(key)); // checks key.ptr_ != NULL |
| sorted_variable_dict[PrintableTemplateString(key)] = |
| PrintableTemplateString(it->second); |
| } |
| for (map<string,string>::const_iterator it = sorted_variable_dict.begin(); |
| it != sorted_variable_dict.end(); ++it) { |
| writer_.Write(it->first + ": >" + it->second + "<\n"); |
| } |
| } |
| |
| |
| template<typename MyMap, typename MySectionDict> |
| void SortSections(MyMap* sorted_section_dict, |
| const MySectionDict& section_dict) { |
| typename MySectionDict::const_iterator it = section_dict.begin(); |
| for (; it != section_dict.end(); ++it) { |
| const TemplateString key = TemplateDictionary::IdToString(it->first); |
| assert(!InvalidTemplateString(key)); // checks key.ptr_ != NULL |
| (*sorted_section_dict)[PrintableTemplateString(key)] = it->second; |
| } |
| } |
| |
| void DumpSectionDict(const SectionDict& section_dict) { |
| map<string, const DictVector*> sorted_section_dict; |
| SortSections(&sorted_section_dict, section_dict); |
| for (map<string, const DictVector*>::const_iterator it = |
| sorted_section_dict.begin(); |
| it != sorted_section_dict.end(); ++it) { |
| for (DictVector::const_iterator it2 = it->second->begin(); |
| it2 != it->second->end(); ++it2) { |
| TemplateDictionary* dict = *it2; |
| writer_.Write("section ", it->first, " (dict ", |
| GetDictNum(it2 - it->second->begin() + 1, |
| it->second->size()), |
| ") -->\n"); |
| writer_.Indent(); |
| DumpToString(*dict); |
| writer_.Dedent(); |
| } |
| } |
| } |
| |
| void DumpIncludeDict(const IncludeDict& include_dict) { |
| map<string, const DictVector*> sorted_include_dict; |
| SortSections(&sorted_include_dict, include_dict); |
| for (map<string, const DictVector*>::const_iterator it = |
| sorted_include_dict.begin(); |
| it != sorted_include_dict.end(); ++it) { |
| for (vector<TemplateDictionary*>::size_type i = 0; |
| i < it->second->size(); ++i) { |
| TemplateDictionary* dict = (*it->second)[i]; |
| string from_name = (dict->filename_ && *dict->filename_) ? |
| string(", from ") + dict->filename_ : |
| string(", **NO FILENAME SET; THIS DICT WILL BE IGNORED**"); |
| writer_.Write("include-template ", it->first, " (dict ", |
| GetDictNum(static_cast<int>(i + 1), it->second->size()), |
| from_name, ") -->\n"); |
| writer_.Indent(); |
| DumpToString(*dict); |
| writer_.Dedent(); |
| } |
| } |
| } |
| |
| string GetDictNum(size_t index, size_t size) const { |
| char buf[64]; // big enough for two ints |
| snprintf(buf, sizeof(buf), "%" PRIuS " of %" PRIuS, index, size); |
| return buf; |
| } |
| |
| IndentedWriter writer_; |
| }; |
| |
| void TemplateDictionary::DumpToString(string* out, int indent) const { |
| DictionaryPrinter printer(out, indent); |
| printer.DumpToString(*this); |
| } |
| |
| void TemplateDictionary::Dump(int indent) const { |
| string out; |
| DumpToString(&out, indent); |
| fwrite(out.data(), 1, out.length(), stdout); |
| fflush(stdout); |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::Memdup() |
| // Copy the input into the arena, so we have a permanent copy of |
| // it. Returns a pointer to the arena-copy, as a TemplateString |
| // (in case the input has internal NULs). |
| // ---------------------------------------------------------------------- |
| |
| TemplateString TemplateDictionary::Memdup(const char* s, size_t slen) { |
| return TemplateString(arena_->MemdupPlusNUL(s, slen), slen); // add a \0 too |
| } |
| |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::GetSectionValue() |
| // TemplateDictionary::IsHiddenSection() |
| // TemplateDictionary::IsHiddenTemplate() |
| // TemplateDictionary::GetIncludeTemplateName() |
| // The 'introspection' routines that tell Expand() what's in the |
| // template dictionary. GetSectionValue() does variable lookup: |
| // first look in this dict, then in parent dicts, etc. IsHidden*() |
| // returns true iff the name is not present in the appropriate |
| // dictionary. None of these functions ever returns NULL. |
| // ---------------------------------------------------------------------- |
| |
| TemplateString TemplateDictionary::GetValue( |
| const TemplateString& variable) const LOCKS_EXCLUDED(g_static_mutex) { |
| for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| if (d->variable_dict_) { |
| if (const TemplateString* it = find_ptr(*d->variable_dict_, variable.GetGlobalId())) |
| return *it; |
| } |
| } |
| |
| // No match in the dict tree. Check the template-global dict. |
| assert(template_global_dict_owner_ != NULL); |
| if (template_global_dict_owner_->template_global_dict_ |
| && template_global_dict_owner_->template_global_dict_->variable_dict_) { |
| const VariableDict* template_global_vars = |
| template_global_dict_owner_->template_global_dict_->variable_dict_; |
| |
| if (const TemplateString* it = find_ptr(*template_global_vars, variable.GetGlobalId())) |
| return *it; |
| } |
| |
| // No match in dict tree or template-global dict. Last chance: global dict. |
| { |
| ReaderMutexLock ml(&g_static_mutex); |
| if (const TemplateString* it = find_ptr(*global_dict_, variable.GetGlobalId())) |
| return *it; |
| return *empty_string_; |
| } |
| } |
| |
| bool TemplateDictionary::IsHiddenSection(const TemplateString& name) const { |
| for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| if (d->section_dict_ && |
| d->section_dict_->count(name.GetGlobalId())) |
| return false; |
| } |
| assert(template_global_dict_owner_ != NULL); |
| if (template_global_dict_owner_->template_global_dict_ && |
| template_global_dict_owner_->template_global_dict_->section_dict_) { |
| SectionDict* sections = |
| template_global_dict_owner_->template_global_dict_->section_dict_; |
| if (sections->count(name.GetGlobalId())) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| bool TemplateDictionary::IsHiddenTemplate(const TemplateString& name) const { |
| for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| if (d->include_dict_ && |
| d->include_dict_->count(name.GetGlobalId())) |
| return false; |
| } |
| return true; |
| } |
| |
| const char *TemplateDictionary::GetIncludeTemplateName( |
| const TemplateString& variable, int dictnum) const { |
| for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| if (d->include_dict_) { |
| if (DictVector* it = find_ptr2(*d->include_dict_, variable.GetGlobalId())) { |
| TemplateDictionary* dict = (*it)[dictnum]; |
| return dict->filename_ ? dict->filename_ : ""; // map NULL to "" |
| } |
| } |
| } |
| assert("Call IsHiddenTemplate before GetIncludeTemplateName" && 0); |
| abort(); |
| } |
| |
| bool TemplateDictionary::Empty() const { |
| if ((variable_dict_ && !variable_dict_->empty()) || |
| (section_dict_ && section_dict_->empty()) || |
| (include_dict_ && include_dict_->empty())) { |
| return false; |
| } |
| return true; |
| } |
| |
| // ---------------------------------------------------------------------- |
| // TemplateDictionary::CreateSectionIterator() |
| // TemplateDictionary::CreateTemplateIterator() |
| // TemplateDictionary::Iterator::HasNext() |
| // TemplateDictionary::Iterator::Next() |
| // Iterator framework. |
| // ---------------------------------------------------------------------- |
| |
| template <typename T> bool TemplateDictionary::Iterator<T>::HasNext() const { |
| return begin_ != end_; |
| } |
| |
| template <typename T> const TemplateDictionaryInterface& |
| TemplateDictionary::Iterator<T>::Next() { |
| return **(begin_++); |
| } |
| |
| TemplateDictionaryInterface::Iterator* |
| TemplateDictionary::CreateTemplateIterator( |
| const TemplateString& section_name) const { |
| for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| if (d->include_dict_) { |
| if (DictVector* it = find_ptr2(*d->include_dict_, section_name.GetGlobalId())) { |
| // Found it! Return it as an Iterator |
| return MakeIterator(*it); |
| } |
| } |
| } |
| assert("Call IsHiddenTemplate before CreateTemplateIterator" && 0); |
| abort(); |
| } |
| |
| TemplateDictionaryInterface::Iterator* |
| TemplateDictionary::CreateSectionIterator( |
| const TemplateString& section_name) const { |
| for (const TemplateDictionary* d = this; d; d = d->parent_dict_) { |
| if (d->section_dict_) { |
| if (const DictVector* it = find_ptr2(*d->section_dict_, section_name.GetGlobalId())) { |
| // Found it! Return it as an Iterator |
| return MakeIterator(*it); |
| } |
| } |
| } |
| // Check the template global dictionary. |
| assert(template_global_dict_owner_); |
| const TemplateDictionary* template_global_dict = |
| template_global_dict_owner_->template_global_dict_; |
| if (template_global_dict && template_global_dict->section_dict_) { |
| if (const DictVector* it = find_ptr2(*template_global_dict->section_dict_, section_name.GetGlobalId())) { |
| return MakeIterator(*it); |
| } |
| } |
| assert("Call IsHiddenSection before GetDictionaries" && 0); |
| abort(); |
| } |
| |
| } |