| // Copyright (c) 2009, 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. |
| |
| // --- |
| // |
| // This file implements the Template Cache used to store templates. |
| |
| #ifndef TEMPLATE_TEMPLATE_CACHE_H_ |
| #define TEMPLATE_TEMPLATE_CACHE_H_ |
| |
| #include <hash_map> // for stdext::hash_map<> |
| #include <string> // for string |
| #include <utility> // for pair |
| #include <vector> // for vector<> |
| #include <ctemplate/template_emitter.h> // for ExpandEmitter, etc |
| #include <ctemplate/template_enums.h> // for Strip |
| #include <ctemplate/template_string.h> |
| #include <ctemplate/per_expand_data.h> |
| namespace ctemplate { |
| class FileStat; |
| } |
| class Mutex; |
| class TemplateCacheUnittest; |
| |
| // NOTE: if you are statically linking the template library into your binary |
| // (rather than using the template .dll), set '/D CTEMPLATE_DLL_DECL=' |
| // as a compiler flag in your project file to turn off the dllimports. |
| #ifndef CTEMPLATE_DLL_DECL |
| # define CTEMPLATE_DLL_DECL __declspec(dllimport) |
| extern template class __declspec(dllimport) std::allocator<std::string>; |
| extern template class __declspec(dllimport) std::vector<std::string>; |
| #else |
| template class __declspec(dllexport) std::allocator<std::string>; |
| template class __declspec(dllexport) std::vector<std::string>; |
| #endif |
| |
| namespace ctemplate { |
| |
| class PerExpandData; |
| class Template; |
| class TemplateCachePeer; |
| class TemplateDictionaryInterface; |
| |
| // A cache to store parsed templates. |
| class CTEMPLATE_DLL_DECL TemplateCache { |
| public: |
| TemplateCache(); |
| ~TemplateCache(); |
| |
| // ---- CREATING A TEMPLATE OBJECT ------- |
| // LoadTemplate |
| // StringToTemplateCache |
| |
| // Attempts to load the template object stored under its filename, |
| // into the template cache. It first checks if the object is already |
| // in the cache. Any object retrieved from the cache is then |
| // checked to see if its status is marked for "reload if changed." |
| // If so, ReloadIfChanged is called on the retrieved object. Returns |
| // true if the object is loaded. Also returns true if the object |
| // already exists, and no reload was required. |
| // |
| // When it fails to retrieve one from the cache, it creates a new |
| // template object, passing the filename and 'strip' values to the |
| // constructor. (See constructor below for the meaning of the |
| // flags.) If it succeeds in creating an object, including loading |
| // and parsing the associated template file, the object is stored in |
| // the cache, and the method returns true. |
| // |
| // If it fails in loading and parsing the template file, either |
| // because the file was not found or it contained syntax errors, |
| // then the newly created object is deleted and the method returns |
| // false. (NOTE: This description is much longer and less precise |
| // and probably harder to understand than the method itself. Read |
| // the code.) |
| // |
| // To enable Auto-Escape on that template, place the corresponding |
| // AUTOESCAPE pragma at the top of the template file. The template |
| // will then be Auto-Escaped independently of the template it may be |
| // included from or the templates it may include. |
| // |
| // 'Strip' indicates how to handle whitespace when expanding the |
| // template. DO_NOT_STRIP keeps the template exactly as-is. |
| // STRIP_BLANK_LINES elides all blank lines in the template. |
| // STRIP_WHITESPACE elides all blank lines, and also all whitespace |
| // at either the beginning or end of a line. See template constructor |
| // for more details. |
| bool LoadTemplate(const TemplateString& filename, Strip strip); |
| |
| // Parses the string as a template file (e.g. "Hello {{WORLD}}"), |
| // and inserts it into the parsed template cache, so it can later be |
| // used by the user. The user specifies a key and a strip, which are |
| // later passed in to expand the template. |
| // Returns true if the template was successfully parsed and |
| // inserted to the template cache, or false otherwise. In particular, |
| // we return false if a string was already cached with the given key. |
| // NOTE: to include this template from within another template (via |
| // "{{>TEMPLATE_THAT_COMES_FROM_A_STRING}}"), the argument you pass |
| // to TemplateDictionary::SetFilename() is the key you used to register |
| // the string-template. |
| bool StringToTemplateCache(const TemplateString& key, |
| const TemplateString& content, |
| Strip strip); |
| bool StringToTemplateCache(const TemplateString& key, |
| const char* content, |
| size_t content_len, |
| Strip strip) { |
| return StringToTemplateCache(key, |
| TemplateString(content, content_len), |
| strip); |
| } |
| |
| // ---- EXPANDING A TEMPLATE ------- |
| // ExpandWithData |
| // ExpandFrozen |
| |
| // This returns false if the expand failed for some reason: filename |
| // could not be found on disk (and isn't already in the cache), or |
| // the template is mal-formed, or a sub-included template couldn't |
| // be found. Note that even if it returns false, it may have emitted |
| // some output to ExpandEmitter, before it noticed the problem. |
| bool ExpandWithData(const TemplateString& filename, Strip strip, |
| const TemplateDictionaryInterface *dictionary, |
| PerExpandData* per_expand_data, |
| ExpandEmitter* output); |
| bool ExpandWithData(const TemplateString& filename, Strip strip, |
| const TemplateDictionaryInterface* dictionary, |
| PerExpandData* per_expand_data, |
| std::string* output_buffer) { |
| if (output_buffer == NULL) return false; |
| StringEmitter e(output_buffer); |
| return ExpandWithData(filename, strip, dictionary, per_expand_data, &e); |
| } |
| |
| // Const version of ExpandWithData, intended for use with frozen |
| // caches. This method returns false if the requested |
| // template-filename is not found in the cache, rather than fetching |
| // the template from disk and continuing, as ExpandWithData does. |
| // (That is why the method can be const.) Likewise, it will return |
| // false, rather than fetch, if any sub-included template filename |
| // is not found in the cache. |
| // Unfortunately, the only way to enforce this last requirement at |
| // the moment is to have the template-cache be Frozen(). So that |
| // is a pre-requisite for calling this method. It may be relaxed |
| // in the future (if we rewrite the control flow to pass around the |
| // necessary state). |
| // Like ExpandWithData(), this may write partial results into output |
| // even if it returns false (due to template error or file not found). |
| bool ExpandNoLoad(const TemplateString& filename, Strip strip, |
| const TemplateDictionaryInterface *dictionary, |
| PerExpandData* per_expand_data, |
| ExpandEmitter* output) const; |
| bool ExpandNoLoad(const TemplateString& filename, Strip strip, |
| const TemplateDictionaryInterface* dictionary, |
| PerExpandData* per_expand_data, |
| std::string* output_buffer) const { |
| if (output_buffer == NULL) return false; |
| StringEmitter e(output_buffer); |
| return ExpandNoLoad(filename, strip, dictionary, per_expand_data, &e); |
| } |
| |
| // ---- FINDING A TEMPLATE FILE ------- |
| |
| // Sets the root directory for all templates used by the program. |
| // After calling this method, the filename passed to GetTemplate may |
| // be a relative pathname (no leading '/'), in which case this |
| // root-directory is prepended to the filename. This clears the old |
| // 'main' root directory, and also all alternate root directories |
| // that may had been added previously. |
| bool SetTemplateRootDirectory(const std::string& directory); |
| |
| // Adds an additional search path for all templates used by the |
| // program. You may call this multiple times. |
| bool AddAlternateTemplateRootDirectory(const std::string& directory); |
| |
| // Returns the 'main' root directory set by SetTemplateRootDirectory(). |
| std::string template_root_directory() const; |
| |
| // Given an unresolved filename, look through the template search |
| // path to see if the template can be found. If so, return the path |
| // of the resolved filename, otherwise return an empty string. |
| std::string FindTemplateFilename(const std::string& unresolved) |
| const; |
| |
| // ---- MANAGING THE CACHE ------- |
| // Freeze |
| // Delete |
| // ClearCache |
| // ReloadAllIfChanged |
| // Clone |
| |
| // Marks the template cache as immutable. After this method is called, |
| // the cache can no longer be modified by loading new templates or |
| // reloading existing templates. During expansion only cached |
| // included templates will be used, they won't be loaded on-demand. |
| void Freeze(); |
| |
| // Delete |
| // Deletes one template object from the cache, if it exists. |
| // This can be used for either file- or string-based templates. |
| // Returns true if the object was deleted, false otherwise. |
| bool Delete(const TemplateString& key); |
| |
| // ClearCache |
| // Deletes all the template objects in the cache and all raw |
| // contents cached from StringToTemplateCache. This should only |
| // be done once, just before exiting the program and after all |
| // template expansions are completed. (If you want to refresh the |
| // cache, the correct method to use is ReloadAllIfChanged, not |
| // this one.) Note: this method is not necessary unless you are |
| // testing for memory leaks. Calling this before exiting the |
| // program will prevent unnecessary reporting in that case. |
| void ClearCache(); |
| |
| // ReloadAllIfChanged |
| // If IMMEDIATE_RELOAD, reloads and parses all templates right away, |
| // if the corresponding template files have changed. |
| // If LAZY_RELOAD, then sets the reload bit on all templates. |
| // Subsequent call to GetTemplate() checks if file has changed, and if so |
| // reloads and parses the file into the cache. |
| // |
| // IMMEDIATE_RELOAD gives a more consistent snapshot of the current |
| // templates, since all templates in the cache are reloaded at |
| // (approximately) the same time. On the other hand, LAZY_RELOAD |
| // causes less of a latency spike, since it does not require |
| // loading lots of templates from disk at the same time. If in |
| // doubt, LAZY_RELOAD is probably a better choice. |
| |
| // If a file with the same name as an existing template-file, is added |
| // in another search path, ReloadAllIfChanged will pick up the file in the |
| // earlier search-path. |
| enum ReloadType { LAZY_RELOAD, IMMEDIATE_RELOAD }; |
| void ReloadAllIfChanged(ReloadType reload_tyle); |
| |
| // Clone |
| // Returns a copy of the cache. It makes a shallow copy of the |
| // parsed_template_cache_, incrementing refcount of templates. |
| // The caller is responsible for deallocating the returned TemplateCache. |
| // NOTE(user): Annotalysis expects this method to have a lock for |
| // a TemplateCache instance local to the method, but we |
| // know that no other threads will have access to the |
| // instance, so ignore thread safety errors. |
| TemplateCache* Clone() const; |
| |
| // ---- INSPECTING THE CACHE ------- |
| // Dump |
| // DumpToString |
| // TODO(csilvers): implement these? |
| |
| private: |
| // TODO(csilvers): nix Template friend once Template::ReloadIfChanged is gone |
| friend class Template; // for ResolveTemplateFilename |
| friend class TemplateTemplateNode; // for ExpandLocked |
| friend class TemplateCachePeer; // for unittests |
| friend class ::TemplateCacheUnittest; // for unittests |
| |
| class RefcountedTemplate; |
| struct CachedTemplate; |
| class TemplateCacheHash; |
| class RefTplPtrHash; |
| // due to a bug(?) in MSVC, TemplateCachePeer won't compile unless this |
| // particular typedef is public. Ugh. |
| public: |
| typedef std::pair<TemplateId, int> TemplateCacheKey; |
| private: |
| typedef stdext::hash_map<TemplateCacheKey, CachedTemplate, TemplateCacheHash> |
| TemplateMap; |
| typedef stdext::hash_map<RefcountedTemplate*, int, RefTplPtrHash> TemplateCallMap; |
| // Where to search for files. |
| typedef std::vector<std::string> TemplateSearchPath; |
| |
| // GetTemplate |
| // This method is deprecated. It exists here because it is called by |
| // Template::GetTemplate. Also this is used in tests. |
| const Template* GetTemplate(const TemplateString& key, Strip strip); |
| |
| bool ResolveTemplateFilename(const std::string& unresolved, |
| std::string* resolved, |
| FileStat* statbuf) const; |
| |
| // This is used only for internal (recursive) calls to Expand due |
| // to internal template-includes. It doesn't try to acquire the |
| // global template_lock again, in template.cc. |
| // TODO(csilvers): remove this when template.cc's g_template_lock goes away. |
| bool ExpandLocked(const TemplateString& filename, Strip strip, |
| ExpandEmitter* output, |
| const TemplateDictionaryInterface *dictionary, |
| PerExpandData* per_expand_data); |
| |
| bool AddAlternateTemplateRootDirectoryHelper( |
| const std::string& directory, |
| bool clear_template_search_path); |
| |
| // DoneWithGetTemplatePtrs |
| // For historical reasons, GetTemplate() returns a raw Template |
| // pointer rather than a refcounted pointer. So it's impossible |
| // for the user to call DecRef on the template when it's done |
| // using it. To make up for that, we provide this routine, which |
| // says "call DecRef()" on *all* Templates ever used by |
| // GetTemplate(). It's safe for the client to call this when it's |
| // done using all templates it's ever retrieved before (via |
| // GetTemplate). Most likely, the user will call this indirectly, |
| // via ClearCache(). |
| // TODO(panicker): Consider making this method public. |
| void DoneWithGetTemplatePtrs(); |
| |
| // ValidTemplateFilename |
| // Validates the user provided filename before constructing the template |
| bool IsValidTemplateFilename(const std::string& filename, |
| std::string* resolved_filename, |
| FileStat* statbuf) const; |
| |
| // GetTemplateLocked |
| // Internal version of GetTemplate. It's used when the function already |
| // has a write-lock on mutex_. It returns a pointer to a refcounted |
| // template (in the cache), or NULL if the template is not found. |
| // Its used by GetTemplate & ForceReloadAllIfChanged. |
| RefcountedTemplate* GetTemplateLocked( |
| const TemplateString& filename, |
| Strip strip, |
| const TemplateCacheKey& key); |
| |
| // Refcount |
| // Testing only. Returns the refcount of a template, given its cache key. |
| int Refcount(const TemplateCacheKey template_cache_key) const; |
| |
| // GetCachedTemplate |
| // Debug only. Returns whether the cache key is in the parsed cache. |
| bool TemplateIsCached(const TemplateCacheKey template_cache_key) const; |
| |
| TemplateMap* parsed_template_cache_; |
| bool is_frozen_; |
| TemplateSearchPath search_path_; |
| |
| // Since GetTemplate() returns a raw pointer, it's impossible for |
| // the caller to call DecRef() on the returned template when it's |
| // done using it. To make up for that, we store each retval of |
| // GetTemplate in this data structure. Then the user can call |
| // DecRef() on all of them at once, via a DoneWithGetTemplatePtrs() |
| // (which they will probably get at via a call to ClearCache()). |
| TemplateCallMap* get_template_calls_; |
| |
| Mutex* const mutex_; |
| Mutex* const search_path_mutex_; |
| |
| // Can't invoke copy constructor or assignment operator |
| TemplateCache(const TemplateCache&); |
| void operator=(const TemplateCache &); |
| }; |
| |
| } |
| |
| #endif // TEMPLATE_TEMPLATE_CACHE_H_ |