// 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 #include "base/mutex.h" // This must go first so we get _XOPEN_SOURCE #include #include #include #include #include // for sort() #include HASH_MAP_H #include #include #include // for pair<> #include #include "base/arena-inl.h" #include "base/thread_annotations.h" #include "indented_writer.h" #include #include #include #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; _START_GOOGLE_NAMESPACE_ // 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 void operator ()(ManualConstructor* 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 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(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(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(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 inline void DoHashInsert(MapType* m, TemplateId id, ValueType value) { (*m)[id] = value; } template inline void DoHashInsert(MapType* m, TemplateId id, TemplateString value) { pair 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 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); } void TemplateDictionary::SetEscapedValueAndShowSection( const TemplateString variable, const TemplateString value, const TemplateModifier& escfn, const TemplateString section_name) { string escaped_string(escfn(value.ptr_, value.length_)); if (escaped_string.empty()) // no value: the do-nothing case return; TemplateDictionary* sub_dict = AddSectionDictionary(section_name); sub_dict->SetValue(variable, escaped_string); } // ---------------------------------------------------------------------- // 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* 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 sorted_global_dict; FillSortedGlobalDictMap(&sorted_global_dict); for (map::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 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::const_iterator it = sorted_variable_dict.begin(); it != sorted_variable_dict.end(); ++it) { writer_.Write(it->first + ": >" + it->second + "<\n"); } } template 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 sorted_section_dict; SortSections(&sorted_section_dict, section_dict); for (map::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 sorted_include_dict; SortSections(&sorted_include_dict, include_dict); for (map::const_iterator it = sorted_include_dict.begin(); it != sorted_include_dict.end(); ++it) { for (vector::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(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 bool TemplateDictionary::Iterator::HasNext() const { return begin_ != end_; } template const TemplateDictionaryInterface& TemplateDictionary::Iterator::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(); } _END_GOOGLE_NAMESPACE_