blob: f05ca190f739829cea60d5eb547d77314d939f85 [file] [log] [blame]
Mike Samuela5291092011-11-04 13:09:21 -04001// 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
Rob Pike6ab6c492011-11-08 15:38:47 -08005package template
Mike Samuela5291092011-11-04 13:09:21 -04006
7import (
8 "fmt"
9 "io"
Rob Pike07ee3cc2011-11-30 17:42:18 -050010 "io/ioutil"
Mike Samuela5291092011-11-04 13:09:21 -040011 "path/filepath"
Rob Pike9a86e242011-11-30 20:11:57 -080012 "sync"
Rob Pike6ab6c492011-11-08 15:38:47 -080013 "text/template"
Rob Piked38cc472011-12-01 09:19:53 -080014 "text/template/parse"
Mike Samuela5291092011-11-04 13:09:21 -040015)
16
Mike Samuela5291092011-11-04 13:09:21 -040017// Template is a specialized template.Template that produces a safe HTML
18// document fragment.
19type Template struct {
20 escaped bool
Rob Pike07ee3cc2011-11-30 17:42:18 -050021 // We could embed the text/template field, but it's safer not to because
22 // we need to keep our version of the name space and the underlying
23 // template's in sync.
Rob Pike9a86e242011-11-30 20:11:57 -080024 text *template.Template
25 *nameSpace // common to all associated templates
26}
27
28// nameSpace is the data structure shared by all templates in an association.
29type nameSpace struct {
30 mu sync.Mutex
31 set map[string]*Template
32}
33
34// Execute applies a parsed template to the specified data object,
35// writing the output to wr.
36func (t *Template) Execute(wr io.Writer, data interface{}) (err error) {
37 t.nameSpace.mu.Lock()
38 if !t.escaped {
39 if err = escapeTemplates(t, t.Name()); err != nil {
40 t.escaped = true
41 }
42 }
43 t.nameSpace.mu.Unlock()
44 if err != nil {
45 return
46 }
47 return t.text.Execute(wr, data)
Mike Samuela5291092011-11-04 13:09:21 -040048}
49
Rob Pike07ee3cc2011-11-30 17:42:18 -050050// ExecuteTemplate applies the template associated with t that has the given name
51// to the specified data object and writes the output to wr.
Rob Pike9a86e242011-11-30 20:11:57 -080052func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) (err error) {
53 t.nameSpace.mu.Lock()
54 tmpl := t.set[name]
Rob Pike07ee3cc2011-11-30 17:42:18 -050055 if tmpl == nil {
Rob Pike9a86e242011-11-30 20:11:57 -080056 t.nameSpace.mu.Unlock()
Rob Pike07ee3cc2011-11-30 17:42:18 -050057 return fmt.Errorf("template: no template %q associated with template %q", name, t.Name())
58 }
59 if !tmpl.escaped {
Rob Pike9a86e242011-11-30 20:11:57 -080060 err = escapeTemplates(tmpl, name)
61 }
62 t.nameSpace.mu.Unlock()
63 if err != nil {
64 return
Mike Samuela5291092011-11-04 13:09:21 -040065 }
Rob Pike07ee3cc2011-11-30 17:42:18 -050066 return tmpl.text.ExecuteTemplate(wr, name, data)
Mike Samuela5291092011-11-04 13:09:21 -040067}
68
Rob Piked38cc472011-12-01 09:19:53 -080069// Parse parses a string into a template. Nested template definitions
70// will be associated with the top-level template t. Parse may be
71// called multiple times to parse definitions of templates to associate
72// with t. It is an error if a resulting template is non-empty (contains
73// content other than template definitions) and would replace a
74// non-empty template with the same name. (In multiple calls to Parse
75// with the same receiver template, only one call can contain text
76// other than space, comments, and template definitions.)
Rob Pike07ee3cc2011-11-30 17:42:18 -050077func (t *Template) Parse(src string) (*Template, error) {
Rob Pike9a86e242011-11-30 20:11:57 -080078 t.nameSpace.mu.Lock()
Rob Pike07ee3cc2011-11-30 17:42:18 -050079 t.escaped = false
Rob Pike9a86e242011-11-30 20:11:57 -080080 t.nameSpace.mu.Unlock()
Rob Pike07ee3cc2011-11-30 17:42:18 -050081 ret, err := t.text.Parse(src)
Mike Samuela5291092011-11-04 13:09:21 -040082 if err != nil {
83 return nil, err
84 }
Rob Pike07ee3cc2011-11-30 17:42:18 -050085 // In general, all the named templates might have changed underfoot.
86 // Regardless, some new ones may have been defined.
87 // The template.Template set has been updated; update ours.
Rob Pike9a86e242011-11-30 20:11:57 -080088 t.nameSpace.mu.Lock()
89 defer t.nameSpace.mu.Unlock()
Rob Pike07ee3cc2011-11-30 17:42:18 -050090 for _, v := range ret.Templates() {
91 name := v.Name()
Rob Pike9a86e242011-11-30 20:11:57 -080092 tmpl := t.set[name]
Rob Pike07ee3cc2011-11-30 17:42:18 -050093 if tmpl == nil {
Rob Pike9a86e242011-11-30 20:11:57 -080094 tmpl = t.new(name)
Rob Pike07ee3cc2011-11-30 17:42:18 -050095 }
96 tmpl.escaped = false
97 tmpl.text = v
Mike Samuela5291092011-11-04 13:09:21 -040098 }
Rob Pike07ee3cc2011-11-30 17:42:18 -050099 return t, nil
Mike Samuela5291092011-11-04 13:09:21 -0400100}
101
Rob Piked38cc472011-12-01 09:19:53 -0800102// AddParseTree is unimplemented.
103func (t *Template) AddParseTree(name string, tree *parse.Tree) error {
104 return fmt.Errorf("html/template: AddParseTree unimplemented")
Rob Pike07ee3cc2011-11-30 17:42:18 -0500105}
106
107// Clone is unimplemented.
108func (t *Template) Clone(name string) error {
109 return fmt.Errorf("html/template: Add unimplemented")
Mike Samuela5291092011-11-04 13:09:21 -0400110}
111
112// New allocates a new HTML template with the given name.
113func New(name string) *Template {
Rob Pike07ee3cc2011-11-30 17:42:18 -0500114 tmpl := &Template{
115 false,
116 template.New(name),
Rob Pike9a86e242011-11-30 20:11:57 -0800117 &nameSpace{
118 set: make(map[string]*Template),
119 },
Rob Pike07ee3cc2011-11-30 17:42:18 -0500120 }
Rob Pike9a86e242011-11-30 20:11:57 -0800121 tmpl.set[name] = tmpl
Rob Pike07ee3cc2011-11-30 17:42:18 -0500122 return tmpl
123}
124
125// New allocates a new HTML template associated with the given one
126// and with the same delimiters. The association, which is transitive,
127// allows one template to invoke another with a {{template}} action.
128func (t *Template) New(name string) *Template {
Rob Pike9a86e242011-11-30 20:11:57 -0800129 t.nameSpace.mu.Lock()
130 defer t.nameSpace.mu.Unlock()
131 return t.new(name)
132}
133
134// new is the implementation of New, without the lock.
135func (t *Template) new(name string) *Template {
Rob Pike07ee3cc2011-11-30 17:42:18 -0500136 tmpl := &Template{
137 false,
138 t.text.New(name),
Rob Pike9a86e242011-11-30 20:11:57 -0800139 t.nameSpace,
Rob Pike07ee3cc2011-11-30 17:42:18 -0500140 }
Rob Pike9a86e242011-11-30 20:11:57 -0800141 tmpl.set[name] = tmpl
Rob Pike07ee3cc2011-11-30 17:42:18 -0500142 return tmpl
143}
144
145// Name returns the name of the template.
146func (t *Template) Name() string {
147 return t.text.Name()
148}
149
150// Funcs adds the elements of the argument map to the template's function map.
151// It panics if a value in the map is not a function with appropriate return
152// type. However, it is legal to overwrite elements of the map. The return
153// value is the template, so calls can be chained.
154func (t *Template) Funcs(funcMap template.FuncMap) *Template {
155 t.text.Funcs(funcMap)
156 return t
157}
158
159// Delims sets the action delimiters to the specified strings, to be used in
160// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template
161// definitions will inherit the settings. An empty delimiter stands for the
162// corresponding default: {{ or }}.
163// The return value is the template, so calls can be chained.
164func (t *Template) Delims(left, right string) *Template {
165 t.text.Delims(left, right)
166 return t
167}
168
169// Lookup returns the template with the given name that is associated with t,
170// or nil if there is no such template.
171func (t *Template) Lookup(name string) *Template {
Rob Pike9a86e242011-11-30 20:11:57 -0800172 t.nameSpace.mu.Lock()
173 defer t.nameSpace.mu.Unlock()
174 return t.set[name]
Mike Samuela5291092011-11-04 13:09:21 -0400175}
176
177// Must panics if err is non-nil in the same way as template.Must.
178func Must(t *Template, err error) *Template {
Rob Pike07ee3cc2011-11-30 17:42:18 -0500179 t.text = template.Must(t.text, err)
Mike Samuela5291092011-11-04 13:09:21 -0400180 return t
181}
182
Rob Pike07ee3cc2011-11-30 17:42:18 -0500183// ParseFiles creates a new Template and parses the template definitions from
184// the named files. The returned template's name will have the (base) name and
185// (parsed) contents of the first file. There must be at least one file.
186// If an error occurs, parsing stops and the returned *Template is nil.
187func ParseFiles(filenames ...string) (*Template, error) {
188 return parseFiles(nil, filenames...)
Mike Samuela5291092011-11-04 13:09:21 -0400189}
190
Rob Pike07ee3cc2011-11-30 17:42:18 -0500191// ParseFiles parses the named files and associates the resulting templates with
192// t. If an error occurs, parsing stops and the returned template is nil;
193// otherwise it is t. There must be at least one file.
194func (t *Template) ParseFiles(filenames ...string) (*Template, error) {
195 return parseFiles(t, filenames...)
Mike Samuela5291092011-11-04 13:09:21 -0400196}
197
Rob Pike07ee3cc2011-11-30 17:42:18 -0500198// parseFiles is the helper for the method and function. If the argument
199// template is nil, it is created from the first file.
200func parseFiles(t *Template, filenames ...string) (*Template, error) {
201 if len(filenames) == 0 {
202 // Not really a problem, but be consistent.
203 return nil, fmt.Errorf("template: no files named in call to ParseFiles")
Mike Samuela5291092011-11-04 13:09:21 -0400204 }
Rob Pike07ee3cc2011-11-30 17:42:18 -0500205 for _, filename := range filenames {
206 b, err := ioutil.ReadFile(filename)
207 if err != nil {
208 return nil, err
209 }
210 s := string(b)
211 name := filepath.Base(filename)
212 // First template becomes return value if not already defined,
213 // and we use that one for subsequent New calls to associate
214 // all the templates together. Also, if this file has the same name
215 // as t, this file becomes the contents of t, so
216 // t, err := New(name).Funcs(xxx).ParseFiles(name)
217 // works. Otherwise we create a new template associated with t.
218 var tmpl *Template
219 if t == nil {
220 t = New(name)
221 }
222 if name == t.Name() {
223 tmpl = t
224 } else {
225 tmpl = t.New(name)
226 }
227 _, err = tmpl.Parse(s)
228 if err != nil {
229 return nil, err
230 }
231 }
232 return t, nil
Mike Samuela5291092011-11-04 13:09:21 -0400233}
234
Rob Pike07ee3cc2011-11-30 17:42:18 -0500235// ParseGlob creates a new Template and parses the template definitions from the
236// files identified by the pattern, which must match at least one file. The
237// returned template will have the (base) name and (parsed) contents of the
238// first file matched by the pattern. ParseGlob is equivalent to calling
239// ParseFiles with the list of files matched by the pattern.
240func ParseGlob(pattern string) (*Template, error) {
241 return parseGlob(nil, pattern)
Mike Samuela5291092011-11-04 13:09:21 -0400242}
243
Rob Pike07ee3cc2011-11-30 17:42:18 -0500244// ParseGlob parses the template definitions in the files identified by the
245// pattern and associates the resulting templates with t. The pattern is
246// processed by filepath.Glob and must match at least one file. ParseGlob is
247// equivalent to calling t.ParseFiles with the list of files matched by the
248// pattern.
249func (t *Template) ParseGlob(pattern string) (*Template, error) {
250 return parseGlob(t, pattern)
Mike Samuela5291092011-11-04 13:09:21 -0400251}
252
Rob Pike07ee3cc2011-11-30 17:42:18 -0500253// parseGlob is the implementation of the function and method ParseGlob.
254func parseGlob(t *Template, pattern string) (*Template, error) {
Mike Samuela5291092011-11-04 13:09:21 -0400255 filenames, err := filepath.Glob(pattern)
256 if err != nil {
257 return nil, err
258 }
259 if len(filenames) == 0 {
Rob Pike07ee3cc2011-11-30 17:42:18 -0500260 return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern)
Mike Samuela5291092011-11-04 13:09:21 -0400261 }
Rob Pike07ee3cc2011-11-30 17:42:18 -0500262 return parseFiles(t, filenames...)
Mike Samuela5291092011-11-04 13:09:21 -0400263}