Home | History | Annotate | Download | only in template
      1 // Copyright 2011 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package template
      6 
      7 import (
      8 	"fmt"
      9 	"io"
     10 	"io/ioutil"
     11 	"path/filepath"
     12 	"sync"
     13 	"text/template"
     14 	"text/template/parse"
     15 )
     16 
     17 // Template is a specialized Template from "text/template" that produces a safe
     18 // HTML document fragment.
     19 type Template struct {
     20 	// Sticky error if escaping fails.
     21 	escapeErr error
     22 	// We could embed the text/template field, but it's safer not to because
     23 	// we need to keep our version of the name space and the underlying
     24 	// template's in sync.
     25 	text *template.Template
     26 	// The underlying template's parse tree, updated to be HTML-safe.
     27 	Tree       *parse.Tree
     28 	*nameSpace // common to all associated templates
     29 }
     30 
     31 // escapeOK is a sentinel value used to indicate valid escaping.
     32 var escapeOK = fmt.Errorf("template escaped correctly")
     33 
     34 // nameSpace is the data structure shared by all templates in an association.
     35 type nameSpace struct {
     36 	mu  sync.Mutex
     37 	set map[string]*Template
     38 }
     39 
     40 // Templates returns a slice of the templates associated with t, including t
     41 // itself.
     42 func (t *Template) Templates() []*Template {
     43 	ns := t.nameSpace
     44 	ns.mu.Lock()
     45 	defer ns.mu.Unlock()
     46 	// Return a slice so we don't expose the map.
     47 	m := make([]*Template, 0, len(ns.set))
     48 	for _, v := range ns.set {
     49 		m = append(m, v)
     50 	}
     51 	return m
     52 }
     53 
     54 // Option sets options for the template. Options are described by
     55 // strings, either a simple string or "key=value". There can be at
     56 // most one equals sign in an option string. If the option string
     57 // is unrecognized or otherwise invalid, Option panics.
     58 //
     59 // Known options:
     60 //
     61 // missingkey: Control the behavior during execution if a map is
     62 // indexed with a key that is not present in the map.
     63 //	"missingkey=default" or "missingkey=invalid"
     64 //		The default behavior: Do nothing and continue execution.
     65 //		If printed, the result of the index operation is the string
     66 //		"<no value>".
     67 //	"missingkey=zero"
     68 //		The operation returns the zero value for the map type's element.
     69 //	"missingkey=error"
     70 //		Execution stops immediately with an error.
     71 //
     72 func (t *Template) Option(opt ...string) *Template {
     73 	t.text.Option(opt...)
     74 	return t
     75 }
     76 
     77 // escape escapes all associated templates.
     78 func (t *Template) escape() error {
     79 	t.nameSpace.mu.Lock()
     80 	defer t.nameSpace.mu.Unlock()
     81 	if t.escapeErr == nil {
     82 		if t.Tree == nil {
     83 			return fmt.Errorf("template: %q is an incomplete or empty template%s", t.Name(), t.text.DefinedTemplates())
     84 		}
     85 		if err := escapeTemplate(t, t.text.Root, t.Name()); err != nil {
     86 			return err
     87 		}
     88 	} else if t.escapeErr != escapeOK {
     89 		return t.escapeErr
     90 	}
     91 	return nil
     92 }
     93 
     94 // Execute applies a parsed template to the specified data object,
     95 // writing the output to wr.
     96 // If an error occurs executing the template or writing its output,
     97 // execution stops, but partial results may already have been written to
     98 // the output writer.
     99 // A template may be executed safely in parallel.
    100 func (t *Template) Execute(wr io.Writer, data interface{}) error {
    101 	if err := t.escape(); err != nil {
    102 		return err
    103 	}
    104 	return t.text.Execute(wr, data)
    105 }
    106 
    107 // ExecuteTemplate applies the template associated with t that has the given
    108 // name to the specified data object and writes the output to wr.
    109 // If an error occurs executing the template or writing its output,
    110 // execution stops, but partial results may already have been written to
    111 // the output writer.
    112 // A template may be executed safely in parallel.
    113 func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
    114 	tmpl, err := t.lookupAndEscapeTemplate(name)
    115 	if err != nil {
    116 		return err
    117 	}
    118 	return tmpl.text.Execute(wr, data)
    119 }
    120 
    121 // lookupAndEscapeTemplate guarantees that the template with the given name
    122 // is escaped, or returns an error if it cannot be. It returns the named
    123 // template.
    124 func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) {
    125 	t.nameSpace.mu.Lock()
    126 	defer t.nameSpace.mu.Unlock()
    127 	tmpl = t.set[name]
    128 	if tmpl == nil {
    129 		return nil, fmt.Errorf("html/template: %q is undefined", name)
    130 	}
    131 	if tmpl.escapeErr != nil && tmpl.escapeErr != escapeOK {
    132 		return nil, tmpl.escapeErr
    133 	}
    134 	if tmpl.text.Tree == nil || tmpl.text.Root == nil {
    135 		return nil, fmt.Errorf("html/template: %q is an incomplete template", name)
    136 	}
    137 	if t.text.Lookup(name) == nil {
    138 		panic("html/template internal error: template escaping out of sync")
    139 	}
    140 	if tmpl.escapeErr == nil {
    141 		err = escapeTemplate(tmpl, tmpl.text.Root, name)
    142 	}
    143 	return tmpl, err
    144 }
    145 
    146 // Parse parses a string into a template. Nested template definitions
    147 // will be associated with the top-level template t. Parse may be
    148 // called multiple times to parse definitions of templates to associate
    149 // with t. It is an error if a resulting template is non-empty (contains
    150 // content other than template definitions) and would replace a
    151 // non-empty template with the same name.  (In multiple calls to Parse
    152 // with the same receiver template, only one call can contain text
    153 // other than space, comments, and template definitions.)
    154 func (t *Template) Parse(src string) (*Template, error) {
    155 	t.nameSpace.mu.Lock()
    156 	t.escapeErr = nil
    157 	t.nameSpace.mu.Unlock()
    158 	ret, err := t.text.Parse(src)
    159 	if err != nil {
    160 		return nil, err
    161 	}
    162 	// In general, all the named templates might have changed underfoot.
    163 	// Regardless, some new ones may have been defined.
    164 	// The template.Template set has been updated; update ours.
    165 	t.nameSpace.mu.Lock()
    166 	defer t.nameSpace.mu.Unlock()
    167 	for _, v := range ret.Templates() {
    168 		name := v.Name()
    169 		tmpl := t.set[name]
    170 		if tmpl == nil {
    171 			tmpl = t.new(name)
    172 		}
    173 		// Restore our record of this text/template to its unescaped original state.
    174 		tmpl.escapeErr = nil
    175 		tmpl.text = v
    176 		tmpl.Tree = v.Tree
    177 	}
    178 	return t, nil
    179 }
    180 
    181 // AddParseTree creates a new template with the name and parse tree
    182 // and associates it with t.
    183 //
    184 // It returns an error if t has already been executed.
    185 func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) {
    186 	t.nameSpace.mu.Lock()
    187 	defer t.nameSpace.mu.Unlock()
    188 	if t.escapeErr != nil {
    189 		return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name())
    190 	}
    191 	text, err := t.text.AddParseTree(name, tree)
    192 	if err != nil {
    193 		return nil, err
    194 	}
    195 	ret := &Template{
    196 		nil,
    197 		text,
    198 		text.Tree,
    199 		t.nameSpace,
    200 	}
    201 	t.set[name] = ret
    202 	return ret, nil
    203 }
    204 
    205 // Clone returns a duplicate of the template, including all associated
    206 // templates. The actual representation is not copied, but the name space of
    207 // associated templates is, so further calls to Parse in the copy will add
    208 // templates to the copy but not to the original. Clone can be used to prepare
    209 // common templates and use them with variant definitions for other templates
    210 // by adding the variants after the clone is made.
    211 //
    212 // It returns an error if t has already been executed.
    213 func (t *Template) Clone() (*Template, error) {
    214 	t.nameSpace.mu.Lock()
    215 	defer t.nameSpace.mu.Unlock()
    216 	if t.escapeErr != nil {
    217 		return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
    218 	}
    219 	textClone, err := t.text.Clone()
    220 	if err != nil {
    221 		return nil, err
    222 	}
    223 	ret := &Template{
    224 		nil,
    225 		textClone,
    226 		textClone.Tree,
    227 		&nameSpace{
    228 			set: make(map[string]*Template),
    229 		},
    230 	}
    231 	for _, x := range textClone.Templates() {
    232 		name := x.Name()
    233 		src := t.set[name]
    234 		if src == nil || src.escapeErr != nil {
    235 			return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name())
    236 		}
    237 		x.Tree = x.Tree.Copy()
    238 		ret.set[name] = &Template{
    239 			nil,
    240 			x,
    241 			x.Tree,
    242 			ret.nameSpace,
    243 		}
    244 	}
    245 	return ret, nil
    246 }
    247 
    248 // New allocates a new HTML template with the given name.
    249 func New(name string) *Template {
    250 	tmpl := &Template{
    251 		nil,
    252 		template.New(name),
    253 		nil,
    254 		&nameSpace{
    255 			set: make(map[string]*Template),
    256 		},
    257 	}
    258 	tmpl.set[name] = tmpl
    259 	return tmpl
    260 }
    261 
    262 // New allocates a new HTML template associated with the given one
    263 // and with the same delimiters. The association, which is transitive,
    264 // allows one template to invoke another with a {{template}} action.
    265 func (t *Template) New(name string) *Template {
    266 	t.nameSpace.mu.Lock()
    267 	defer t.nameSpace.mu.Unlock()
    268 	return t.new(name)
    269 }
    270 
    271 // new is the implementation of New, without the lock.
    272 func (t *Template) new(name string) *Template {
    273 	tmpl := &Template{
    274 		nil,
    275 		t.text.New(name),
    276 		nil,
    277 		t.nameSpace,
    278 	}
    279 	tmpl.set[name] = tmpl
    280 	return tmpl
    281 }
    282 
    283 // Name returns the name of the template.
    284 func (t *Template) Name() string {
    285 	return t.text.Name()
    286 }
    287 
    288 // FuncMap is the type of the map defining the mapping from names to
    289 // functions. Each function must have either a single return value, or two
    290 // return values of which the second has type error. In that case, if the
    291 // second (error) argument evaluates to non-nil during execution, execution
    292 // terminates and Execute returns that error. FuncMap has the same base type
    293 // as FuncMap in "text/template", copied here so clients need not import
    294 // "text/template".
    295 type FuncMap map[string]interface{}
    296 
    297 // Funcs adds the elements of the argument map to the template's function map.
    298 // It panics if a value in the map is not a function with appropriate return
    299 // type. However, it is legal to overwrite elements of the map. The return
    300 // value is the template, so calls can be chained.
    301 func (t *Template) Funcs(funcMap FuncMap) *Template {
    302 	t.text.Funcs(template.FuncMap(funcMap))
    303 	return t
    304 }
    305 
    306 // Delims sets the action delimiters to the specified strings, to be used in
    307 // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
    308 // definitions will inherit the settings. An empty delimiter stands for the
    309 // corresponding default: {{ or }}.
    310 // The return value is the template, so calls can be chained.
    311 func (t *Template) Delims(left, right string) *Template {
    312 	t.text.Delims(left, right)
    313 	return t
    314 }
    315 
    316 // Lookup returns the template with the given name that is associated with t,
    317 // or nil if there is no such template.
    318 func (t *Template) Lookup(name string) *Template {
    319 	t.nameSpace.mu.Lock()
    320 	defer t.nameSpace.mu.Unlock()
    321 	return t.set[name]
    322 }
    323 
    324 // Must is a helper that wraps a call to a function returning (*Template, error)
    325 // and panics if the error is non-nil. It is intended for use in variable initializations
    326 // such as
    327 //	var t = template.Must(template.New("name").Parse("html"))
    328 func Must(t *Template, err error) *Template {
    329 	if err != nil {
    330 		panic(err)
    331 	}
    332 	return t
    333 }
    334 
    335 // ParseFiles creates a new Template and parses the template definitions from
    336 // the named files. The returned template's name will have the (base) name and
    337 // (parsed) contents of the first file. There must be at least one file.
    338 // If an error occurs, parsing stops and the returned *Template is nil.
    339 func ParseFiles(filenames ...string) (*Template, error) {
    340 	return parseFiles(nil, filenames...)
    341 }
    342 
    343 // ParseFiles parses the named files and associates the resulting templates with
    344 // t. If an error occurs, parsing stops and the returned template is nil;
    345 // otherwise it is t. There must be at least one file.
    346 func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
    347 	return parseFiles(t, filenames...)
    348 }
    349 
    350 // parseFiles is the helper for the method and function. If the argument
    351 // template is nil, it is created from the first file.
    352 func parseFiles(t *Template, filenames ...string) (*Template, error) {
    353 	if len(filenames) == 0 {
    354 		// Not really a problem, but be consistent.
    355 		return nil, fmt.Errorf("html/template: no files named in call to ParseFiles")
    356 	}
    357 	for _, filename := range filenames {
    358 		b, err := ioutil.ReadFile(filename)
    359 		if err != nil {
    360 			return nil, err
    361 		}
    362 		s := string(b)
    363 		name := filepath.Base(filename)
    364 		// First template becomes return value if not already defined,
    365 		// and we use that one for subsequent New calls to associate
    366 		// all the templates together. Also, if this file has the same name
    367 		// as t, this file becomes the contents of t, so
    368 		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
    369 		// works. Otherwise we create a new template associated with t.
    370 		var tmpl *Template
    371 		if t == nil {
    372 			t = New(name)
    373 		}
    374 		if name == t.Name() {
    375 			tmpl = t
    376 		} else {
    377 			tmpl = t.New(name)
    378 		}
    379 		_, err = tmpl.Parse(s)
    380 		if err != nil {
    381 			return nil, err
    382 		}
    383 	}
    384 	return t, nil
    385 }
    386 
    387 // ParseGlob creates a new Template and parses the template definitions from the
    388 // files identified by the pattern, which must match at least one file. The
    389 // returned template will have the (base) name and (parsed) contents of the
    390 // first file matched by the pattern. ParseGlob is equivalent to calling
    391 // ParseFiles with the list of files matched by the pattern.
    392 func ParseGlob(pattern string) (*Template, error) {
    393 	return parseGlob(nil, pattern)
    394 }
    395 
    396 // ParseGlob parses the template definitions in the files identified by the
    397 // pattern and associates the resulting templates with t. The pattern is
    398 // processed by filepath.Glob and must match at least one file. ParseGlob is
    399 // equivalent to calling t.ParseFiles with the list of files matched by the
    400 // pattern.
    401 func (t *Template) ParseGlob(pattern string) (*Template, error) {
    402 	return parseGlob(t, pattern)
    403 }
    404 
    405 // parseGlob is the implementation of the function and method ParseGlob.
    406 func parseGlob(t *Template, pattern string) (*Template, error) {
    407 	filenames, err := filepath.Glob(pattern)
    408 	if err != nil {
    409 		return nil, err
    410 	}
    411 	if len(filenames) == 0 {
    412 		return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern)
    413 	}
    414 	return parseFiles(t, filenames...)
    415 }
    416