blob: 4d12cb753536a399c21119483fbee1f373011441 [file] [log] [blame]
// 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.
// ---
//
// This file implements the Template class. For information about
// how to use this class, and to write the templates it takes as input,
// see the doc/ directory.
#ifndef CTEMPLATE_TEMPLATE_H_
#define CTEMPLATE_TEMPLATE_H_
#include <time.h> // for time_t
#include <string>
#include <ctemplate/template_cache.h>
#include <ctemplate/template_enums.h>
#include <ctemplate/template_string.h>
// We include this just so folks don't have to include both template.h
// and template_dictionary.h, or template_namelist.h etc, to use the
// template system; we don't actually use anything in these files
// ourselves.
#if 1
#include <ctemplate/template_dictionary.h>
#include <ctemplate/template_namelist.h>
#include <ctemplate/per_expand_data.h>
#else
namespace ctemplate {
class TemplateDictionaryInterface;
class PerExpandData;
}
#endif
namespace google_ctemplate_streamhtmlparser {
class HtmlParser;
}
// 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)
#endif
namespace ctemplate {
// These free functions form the "simple" template API, and support
// the most common operations (expanding a template from a file, and
// from a string). They all just delegate to a default instance of
// the TemplateCache object.
//
// For more sophisticated use of the template system, you may need
// to create your own TemplateCache object, and work directly with
// it. See template_cache.h for details.
extern CTEMPLATE_DLL_DECL const TemplateCache* default_template_cache();
extern CTEMPLATE_DLL_DECL TemplateCache* mutable_default_template_cache();
// ---- EXPANDING A TEMPLATE -------
// ExpandTemplate
// ExpandWithData
// Loads the template named filename from disk if necessary -- it
// gets it from the cache instead, if the template had been loaded
// before or if it had been put explicitly in the cache via a call
// to StringToTemplateCache() -- and expands it using the given
// dictionary.
// The first version is the most general, followed by common-case code.
inline bool ExpandTemplate(const TemplateString& filename, Strip strip,
const TemplateDictionaryInterface *dictionary,
ExpandEmitter* output) {
return mutable_default_template_cache()->ExpandWithData(
filename, strip, dictionary, NULL, output);
}
inline bool ExpandTemplate(const TemplateString& filename, Strip strip,
const TemplateDictionaryInterface* dictionary,
std::string* output_buffer) {
return mutable_default_template_cache()->ExpandWithData(
filename, strip, dictionary, NULL, output_buffer);
}
// If you want any per-expand data to be used at expand time, call
// this routine instead of Expand. You pass in an extra
// PerExpandData structure (see per_expand_data.h) which sets this
// data: whether or not you want the template to be annotated, and
// any data you want to pass in to template modifers. If
// per_expand_data is NULL, this is exactly the same as Expand().
// The first version is the most general, followed by common-case code.
inline bool ExpandWithData(const TemplateString& filename, Strip strip,
const TemplateDictionaryInterface *dictionary,
PerExpandData* per_expand_data,
ExpandEmitter* output) {
return mutable_default_template_cache()->ExpandWithData(
filename, strip, dictionary, per_expand_data, output);
}
inline bool ExpandWithData(const TemplateString& filename, Strip strip,
const TemplateDictionaryInterface* dictionary,
PerExpandData* per_expand_data,
std::string* output_buffer) {
return mutable_default_template_cache()->ExpandWithData(
filename, strip, dictionary, per_expand_data, output_buffer);
}
// ---- INSERTING INTO THE CACHE -------
// LoadTemplate
// StringToTemplateCache
// Reads a file from disk and inserts it into the template, if it's
// not already there. Returns true on success or false if the
// template could not be found, or could not be parsed. It's never
// necessary to call this -- Expand() will load templates lazily if
// needed -- but you may want to if you want to make sure templates
// exist before trying to expand them, or because you want to
// control disk access patterns, or for some other reason.
inline bool LoadTemplate(const TemplateString& filename, Strip strip) {
return mutable_default_template_cache()->LoadTemplate(filename, strip);
}
// Inserts the given string into the default template cache, as if
// it were a file read from disk. You can call Expand() with its
// first arg (filename) the same as the key you use here. You can
// also use this key as the 'filename' for sub-included templates,
// in TemplateDictionary::SetFilename().
inline bool StringToTemplateCache(const TemplateString& key,
const TemplateString& content,
Strip strip) {
return mutable_default_template_cache()->StringToTemplateCache(
key, content, strip);
}
inline bool StringToTemplateCache(const TemplateString& key,
const char* content, size_t content_len,
Strip strip) {
return mutable_default_template_cache()->StringToTemplateCache(
key, content, content_len, strip);
}
// ---------------------------------------------------------------------
// The follow are deprecated.
// TODO(csilvers): move to parsed_template.h
// TemplateState of a template is:
// - TS_EMPTY before parsing is complete,
// - TS_ERROR if a syntax error was found during parsing, and
// - TS_READY if parsing has completed successfully
// (TS_UNUSED is not used)
enum TemplateState { TS_UNUSED, TS_EMPTY, TS_ERROR, TS_READY };
// Used for Auto-Escape. It represents the different contexts a template may
// be initialized in via the AUTOESCAPE pragma in the template file
// (or string). It is only public for testing. The contexts are:
// - TC_HTML: The template contains HTML code. Need not be a complete HTML
// page just content the browser interprets in the context of
// HTML parsing. This should be the most common context to use.
// This mode activates our HTML parser.
// - TC_JS: The template contains raw javascript. If your template
// starts with <script> tag, it is of type TC_HTML not TC_JS.
// TC_JS is typically associated with a content-type of
// text/javascript. This mode activates our HTML parser.
// - TC_CSS: The template contains CSS (cascaded style-sheet). If your
// template starts with a <style> tag, it is of type TC_HTML
// not TC_CSS. A TC_CSS template is typically associated with a
// text/css content-type header. Currently treated same as
// TC_HTML but don't rely on that. We may later develop
// CSS-specific sanitizers and parsers.
// - TC_JSON: The template contains raw JSON. Applies javascript_escape
// to variables. Note: javascript_escape is safer than
// json_escape which we may want to remove.
// - TC_XML: The template contains raw XML. Applies xml_escape to variables.
// CAUTION: This mode is not suitable for cases where the
// application data encapsulated in XML requires special
// escaping, such as the case of XHTML.
// TC_XML is typically associated with text/xml content-type.
// - TC_MANUAL: Equivalent to not specifying auto-escaping at all.
//
// TODO(csilvers): Make this a private part of the Template class.
enum TemplateContext { TC_UNUSED, TC_HTML, TC_JS, TC_CSS, TC_JSON,
TC_XML, TC_MANUAL };
// This class is deprecated. Old code uses this class heavily (via
// GetTemplate() to obtain a Template*, and then methods on that
// Template*) but new code should use the free functions above.
class CTEMPLATE_DLL_DECL Template {
public:
// ---- METHODS FOR TOOLS ----
// These are not intended for normal use, but are public so a
// tool can use them.
// Used by make_tpl_varnames_h.cc.
void WriteHeaderEntries(std::string *outstring) const;
// ---- DEPRECATED METHODS ----
// These methods used to be the primary way of using the Template
// object, but have been deprecated in favor of the (static)
// methods above. If you are using these deprecated methods,
// consider moving to the above methods instead, or to moving to
// using your own TemplateCache (which supports richer operations
// on parsed templates).
// Loads a template from disk or cache or string, and returns the Template*.
// INSTEAD, use the static Expand that takes a filename.
static Template *GetTemplate(const TemplateString& filename, Strip strip);
virtual ~Template(); // when the time comes to delete these Template*'s.
// Parses a string immediately and returns the resulting Template*.
// You can call the (deprecated) non-static Expand() method on this
// template in order to expand it with a dictionary. You are
// responsible for deleting the Template* when you are done with it.
// INSTEAD, use StringToTemplateCache (with a key) plus the static Expand().
// TOOO(csilvers): return a const Template* instead.
static Template* StringToTemplate(const TemplateString& content,
Strip strip);
static Template* StringToTemplate(const char* content, size_t content_len,
Strip strip) {
return StringToTemplate(TemplateString(content, content_len), strip);
}
// Non-static Expand*() works on a Template* returned from GetTemplate().
// INSTEAD, use static expand with a filename (or key-name for strings).
bool ExpandWithData(ExpandEmitter* output,
const TemplateDictionaryInterface* dictionary,
PerExpandData* per_expand_data) const {
return ExpandWithDataAndCache(output, dictionary, per_expand_data,
default_template_cache());
}
bool ExpandWithData(std::string* output_buffer,
const TemplateDictionaryInterface* dictionary,
PerExpandData* per_expand_data) const {
if (output_buffer == NULL) return false;
StringEmitter e(output_buffer);
return ExpandWithData(&e, dictionary, per_expand_data);
}
bool Expand(ExpandEmitter* output,
const TemplateDictionaryInterface* dictionary) const {
return ExpandWithData(output, dictionary, NULL);
}
bool Expand(std::string* output_buffer,
const TemplateDictionaryInterface* dictionary) const {
return ExpandWithData(output_buffer, dictionary, NULL);
}
// Dump to stdout or a string. filename is just used to annotate output.
void Dump(const char *filename) const;
void DumpToString(const char *filename, std::string *out) const;
// Retrieves the state, template-file, or strip mode of this Template.
TemplateState state() const;
const char *template_file() const;
const char *original_filename() const;
Strip strip() const;
// Work at the level of groups of templates, so just call through to
// the default TemplateCache; see template_cache.h for what these do.
// INSTEAD, create your own TemplateCache and call these methods on that.
static bool SetTemplateRootDirectory(const std::string& dir) {
return mutable_default_template_cache()->SetTemplateRootDirectory(dir);
}
static bool AddAlternateTemplateRootDirectory(const std::string& dir) {
return mutable_default_template_cache()->AddAlternateTemplateRootDirectory(
dir);
}
static std::string template_root_directory() {
return default_template_cache()->template_root_directory();
}
static std::string FindTemplateFilename(
const std::string& unresolved) {
return default_template_cache()->FindTemplateFilename(unresolved);
}
static void RemoveStringFromTemplateCache(const std::string& key) {
mutable_default_template_cache()->Delete(key);
}
static void ClearCache() {
mutable_default_template_cache()->ClearCache();
}
static void ReloadAllIfChanged() {
mutable_default_template_cache()->ReloadAllIfChanged(
TemplateCache::LAZY_RELOAD);
}
// ---- EXTRA-DEPRECATED METHODS ----
// These methods were deprecated even before the move to
// TemplateCache. We'd really like you to move from these to one
// of the "approved" methods, or even one of the deprecated
// methods. Comments here don't even describe what these
// functions do, just how to transition off of using them.
// INSTEAD, use the StringToTemplateCache function that takes the strip mode.
static bool StringToTemplateCache(const TemplateString& key,
const TemplateString& content);
static bool StringToTemplateCache(const TemplateString& key,
const char* content, size_t content_len) {
return StringToTemplateCache(key, TemplateString(content, content_len));
}
// This is to prevent against typos: you want the global (free-function)
// StringToTemplateCache here, not the one in Template.
static bool StringToTemplateCache(const TemplateString& key,
const char* content, Strip);
// INSTEAD, use ReloadAllIfChanged.
bool ReloadIfChanged();
protected:
friend class SectionTemplateNode; // for access to set_state(), ParseState
friend class TemplateTemplateNode; // for recursive call to Expand()
// Template constructor
// Reads the template file and parses it into a parse tree of TemplateNodes
// by calling the method ReloadIfChanged
// The top node is a section node with the arbitrary name "__{{MAIN}}__"
// '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. It also removes
// any linefeed (possibly following whitespace) that follows a closing
// '}}' of any kind of template marker EXCEPT a template variable.
// This means a linefeed may be removed anywhere by simply placing
// a comment marker as the last element on the line.
// These two options allow the template to include whitespace for
// readability without adding to the expanded output.
Template(const TemplateString& filename, Strip strip, TemplateCache* owner);
// MaybeInitHtmlParser
// In TemplateContexts where the HTML parser is needed, we
// initialize it in the appropriate mode. Also we do a sanity
// check (cannot fail) on the template filename. This function is
// called at most once for a Template. In_tag is only meaningful
// for TC_HTML: It is true for templates that start inside an HTML
// tag and hence are expected to contain HTML attribute name/value
// pairs only. It is false for standard HTML templates.
void MaybeInitHtmlParser(bool in_tag);
// BuildTree
// Parses the contents of the file (retrieved via ReloadIfChanged)
// and stores the resulting parse structure in tree_. Returns true
// iff the tree-builder encountered no errors. Note: takes
// ownership of input_buffer, and will delete it. It should have
// been created via new[].
bool BuildTree(const char *input_buffer, const char* input_buffer_end);
// Internal version of ReloadIfChanged, used when the function already
// has a write-lock on g_template_mutex.
bool ReloadIfChangedLocked();
// set_state
// Sets the state of the template. Used during BuildTree().
void set_state(TemplateState new_state);
// StripBuffer
// Modifies buffer in-place based on the strip_ mode, to remove
// extra whitespace. May delete[] the input buffer and replace
// it with a new buffer. Used by ReloadIfChanged().
void StripBuffer(char **buffer, size_t* len);
// The file we originally got from the Template() constructor
const std::string original_filename_;
// The pathname as fully resolved on the filesystem
std::string resolved_filename_;
time_t filename_mtime_; // lastmod time for filename last time we loaded it
// What to do with whitespace at template-expand time
Strip strip_;
// Keeps track of where we are in reloading, or if there was an error loading
TemplateState state_;
// The cache we got this template from. This is not well-defined: a
// Template can be in more than one cache.
// TODO(csilvers): remove this once we deprecate the one user, which
// is ReloadIfChanged.
TemplateCache* template_cache_;
// The current template-contents, as read from the file
const char* template_text_;
int template_text_len_;
// The current parsed template structure. Has pointers into template_text_.
class SectionTemplateNode *tree_; // defined in template.cc
// Template markers have the form {{VARIABLE}}, etc. These constants
// define the {{ and }} that delimit template markers.
struct CTEMPLATE_DLL_DECL MarkerDelimiters {
const char* start_marker;
size_t start_marker_len;
const char* end_marker;
size_t end_marker_len;
MarkerDelimiters() {
start_marker = "{{"; // The default start-marker
start_marker_len = strlen(start_marker);
end_marker = "}}";
end_marker_len = strlen(end_marker);
}
};
// The current parsing state. Used in BuildTree() and subroutines
struct CTEMPLATE_DLL_DECL ParseState {
const char* bufstart;
const char* bufend;
enum { PS_UNUSED, GETTING_TEXT, GETTING_NAME } phase;
MarkerDelimiters current_delimiters;
ParseState()
: bufstart(NULL), bufend(NULL), phase(PS_UNUSED), current_delimiters()
{}
};
ParseState parse_state_;
// All templates are initialized to TC_MANUAL (no Auto-Escape). Then,
// during template parsing (BuildTree()), if an AUTOESCAPE pragma is
// encountered, the context changes appropriately.
TemplateContext initial_context_;
// Non-null if the template was initialized in an Auto-Escape mode that
// requires a parser (currently TC_HTML, TC_CSS and TC_JS).
google_ctemplate_streamhtmlparser::HtmlParser *htmlparser_;
// A sorted list of trusted variable names, declared here because a unittest
// needs to verify that it is appropriately sorted (an unsorted array would
// lead to the binary search of this array failing).
static const char * const kSafeWhitelistedVariables[];
static const size_t kNumSafeWhitelistedVariables;
private:
friend class TemplateCache;
friend class TemplateCachePeer; // to access num_deletes_
// Internal implementation of Expand
bool ExpandWithDataAndCache(ExpandEmitter* output,
const TemplateDictionaryInterface *dictionary,
PerExpandData* per_expand_data,
const TemplateCache* cache) const;
// This is called for recursive expands, when we already hold template_lock.
bool ExpandLocked(ExpandEmitter* output,
const TemplateDictionaryInterface *dictionary,
PerExpandData* per_expand_data,
const TemplateCache* cache) const;
// Returns the lastmod time in mtime_
// For string-based templates, not backed by a file, this returns 0
time_t mtime() const;
// These are helper routines to StripFile. I would make them static
// inside template.cc, but they use the MarerDelimiters struct.
static bool ParseDelimiters(const char* text, size_t textlen,
MarkerDelimiters* delim);
static bool IsBlankOrOnlyHasOneRemovableMarker(const char** line, size_t* len,
const MarkerDelimiters& delim);
static size_t InsertLine(const char *line, size_t len, Strip strip,
const MarkerDelimiters& delim, char* buffer);
// This is only used by template_cache_test, via TemplateCachePeer.
static int num_deletes() { return num_deletes_; }
static int num_deletes_; // how many times the destructor has been called
// Can't invoke copy constructor or assignment operator
Template(const Template&);
void operator=(const Template &);
};
}
#endif // CTEMPLATE_TEMPLATE_H_