Source file src/pkg/html/template/template.go
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.Template that produces a safe HTML 18 // document fragment. 19 type Template struct { 20 escaped bool 21 // 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. 24 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. 29 type nameSpace struct { 30 mu sync.Mutex 31 set map[string]*Template 32 } 33 34 // Templates returns a slice of the templates associated with t, including t 35 // itself. 36 func (t *Template) Templates() []*Template { 37 ns := t.nameSpace 38 ns.mu.Lock() 39 defer ns.mu.Unlock() 40 // Return a slice so we don't expose the map. 41 m := make([]*Template, 0, len(ns.set)) 42 for _, v := range ns.set { 43 m = append(m, v) 44 } 45 return m 46 } 47 48 // Execute applies a parsed template to the specified data object, 49 // writing the output to wr. 50 func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { 51 t.nameSpace.mu.Lock() 52 if !t.escaped { 53 if err = escapeTemplates(t, t.Name()); err != nil { 54 t.escaped = true 55 } 56 } 57 t.nameSpace.mu.Unlock() 58 if err != nil { 59 return 60 } 61 return t.text.Execute(wr, data) 62 } 63 64 // ExecuteTemplate applies the template associated with t that has the given 65 // name to the specified data object and writes the output to wr. 66 func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error { 67 tmpl, err := t.lookupAndEscapeTemplate(name) 68 if err != nil { 69 return err 70 } 71 return tmpl.text.Execute(wr, data) 72 } 73 74 // lookupAndEscapeTemplate guarantees that the template with the given name 75 // is escaped, or returns an error if it cannot be. It returns the named 76 // template. 77 func (t *Template) lookupAndEscapeTemplate(name string) (tmpl *Template, err error) { 78 t.nameSpace.mu.Lock() 79 defer t.nameSpace.mu.Unlock() 80 tmpl = t.set[name] 81 if tmpl == nil { 82 return nil, fmt.Errorf("html/template: %q is undefined", name) 83 } 84 if tmpl.text.Tree == nil || tmpl.text.Root == nil { 85 return nil, fmt.Errorf("html/template: %q is an incomplete template", name) 86 } 87 if t.text.Lookup(name) == nil { 88 panic("html/template internal error: template escaping out of sync") 89 } 90 if tmpl != nil && !tmpl.escaped { 91 err = escapeTemplates(tmpl, name) 92 } 93 return tmpl, err 94 } 95 96 // Parse parses a string into a template. Nested template definitions 97 // will be associated with the top-level template t. Parse may be 98 // called multiple times to parse definitions of templates to associate 99 // with t. It is an error if a resulting template is non-empty (contains 100 // content other than template definitions) and would replace a 101 // non-empty template with the same name. (In multiple calls to Parse 102 // with the same receiver template, only one call can contain text 103 // other than space, comments, and template definitions.) 104 func (t *Template) Parse(src string) (*Template, error) { 105 t.nameSpace.mu.Lock() 106 t.escaped = false 107 t.nameSpace.mu.Unlock() 108 ret, err := t.text.Parse(src) 109 if err != nil { 110 return nil, err 111 } 112 // In general, all the named templates might have changed underfoot. 113 // Regardless, some new ones may have been defined. 114 // The template.Template set has been updated; update ours. 115 t.nameSpace.mu.Lock() 116 defer t.nameSpace.mu.Unlock() 117 for _, v := range ret.Templates() { 118 name := v.Name() 119 tmpl := t.set[name] 120 if tmpl == nil { 121 tmpl = t.new(name) 122 } 123 tmpl.escaped = false 124 tmpl.text = v 125 } 126 return t, nil 127 } 128 129 // AddParseTree creates a new template with the name and parse tree 130 // and associates it with t. 131 // 132 // It returns an error if t has already been executed. 133 func (t *Template) AddParseTree(name string, tree *parse.Tree) (*Template, error) { 134 t.nameSpace.mu.Lock() 135 defer t.nameSpace.mu.Unlock() 136 if t.escaped { 137 return nil, fmt.Errorf("html/template: cannot AddParseTree to %q after it has executed", t.Name()) 138 } 139 text, err := t.text.AddParseTree(name, tree) 140 if err != nil { 141 return nil, err 142 } 143 ret := &Template{ 144 false, 145 text, 146 t.nameSpace, 147 } 148 t.set[name] = ret 149 return ret, nil 150 } 151 152 // Clone returns a duplicate of the template, including all associated 153 // templates. The actual representation is not copied, but the name space of 154 // associated templates is, so further calls to Parse in the copy will add 155 // templates to the copy but not to the original. Clone can be used to prepare 156 // common templates and use them with variant definitions for other templates 157 // by adding the variants after the clone is made. 158 // 159 // It returns an error if t has already been executed. 160 func (t *Template) Clone() (*Template, error) { 161 t.nameSpace.mu.Lock() 162 defer t.nameSpace.mu.Unlock() 163 if t.escaped { 164 return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name()) 165 } 166 textClone, err := t.text.Clone() 167 if err != nil { 168 return nil, err 169 } 170 ret := &Template{ 171 false, 172 textClone, 173 &nameSpace{ 174 set: make(map[string]*Template), 175 }, 176 } 177 for _, x := range textClone.Templates() { 178 name := x.Name() 179 src := t.set[name] 180 if src == nil || src.escaped { 181 return nil, fmt.Errorf("html/template: cannot Clone %q after it has executed", t.Name()) 182 } 183 if x.Tree != nil { 184 x.Tree = &parse.Tree{ 185 Name: x.Tree.Name, 186 Root: x.Tree.Root.CopyList(), 187 } 188 } 189 ret.set[name] = &Template{ 190 false, 191 x, 192 ret.nameSpace, 193 } 194 } 195 return ret, nil 196 } 197 198 // New allocates a new HTML template with the given name. 199 func New(name string) *Template { 200 tmpl := &Template{ 201 false, 202 template.New(name), 203 &nameSpace{ 204 set: make(map[string]*Template), 205 }, 206 } 207 tmpl.set[name] = tmpl 208 return tmpl 209 } 210 211 // New allocates a new HTML template associated with the given one 212 // and with the same delimiters. The association, which is transitive, 213 // allows one template to invoke another with a {{template}} action. 214 func (t *Template) New(name string) *Template { 215 t.nameSpace.mu.Lock() 216 defer t.nameSpace.mu.Unlock() 217 return t.new(name) 218 } 219 220 // new is the implementation of New, without the lock. 221 func (t *Template) new(name string) *Template { 222 tmpl := &Template{ 223 false, 224 t.text.New(name), 225 t.nameSpace, 226 } 227 tmpl.set[name] = tmpl 228 return tmpl 229 } 230 231 // Name returns the name of the template. 232 func (t *Template) Name() string { 233 return t.text.Name() 234 } 235 236 // FuncMap is the type of the map defining the mapping from names to 237 // functions. Each function must have either a single return value, or two 238 // return values of which the second has type error. In that case, if the 239 // second (error) argument evaluates to non-nil during execution, execution 240 // terminates and Execute returns that error. FuncMap has the same base type 241 // as template.FuncMap, copied here so clients need not import "text/template". 242 type FuncMap map[string]interface{} 243 244 // Funcs adds the elements of the argument map to the template's function map. 245 // It panics if a value in the map is not a function with appropriate return 246 // type. However, it is legal to overwrite elements of the map. The return 247 // value is the template, so calls can be chained. 248 func (t *Template) Funcs(funcMap FuncMap) *Template { 249 t.text.Funcs(template.FuncMap(funcMap)) 250 return t 251 } 252 253 // Delims sets the action delimiters to the specified strings, to be used in 254 // subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template 255 // definitions will inherit the settings. An empty delimiter stands for the 256 // corresponding default: {{ or }}. 257 // The return value is the template, so calls can be chained. 258 func (t *Template) Delims(left, right string) *Template { 259 t.text.Delims(left, right) 260 return t 261 } 262 263 // Lookup returns the template with the given name that is associated with t, 264 // or nil if there is no such template. 265 func (t *Template) Lookup(name string) *Template { 266 t.nameSpace.mu.Lock() 267 defer t.nameSpace.mu.Unlock() 268 return t.set[name] 269 } 270 271 // Must panics if err is non-nil in the same way as template.Must. 272 func Must(t *Template, err error) *Template { 273 if err != nil { 274 panic(err) 275 } 276 return t 277 } 278 279 // ParseFiles creates a new Template and parses the template definitions from 280 // the named files. The returned template's name will have the (base) name and 281 // (parsed) contents of the first file. There must be at least one file. 282 // If an error occurs, parsing stops and the returned *Template is nil. 283 func ParseFiles(filenames ...string) (*Template, error) { 284 return parseFiles(nil, filenames...) 285 } 286 287 // ParseFiles parses the named files and associates the resulting templates with 288 // t. If an error occurs, parsing stops and the returned template is nil; 289 // otherwise it is t. There must be at least one file. 290 func (t *Template) ParseFiles(filenames ...string) (*Template, error) { 291 return parseFiles(t, filenames...) 292 } 293 294 // parseFiles is the helper for the method and function. If the argument 295 // template is nil, it is created from the first file. 296 func parseFiles(t *Template, filenames ...string) (*Template, error) { 297 if len(filenames) == 0 { 298 // Not really a problem, but be consistent. 299 return nil, fmt.Errorf("html/template: no files named in call to ParseFiles") 300 } 301 for _, filename := range filenames { 302 b, err := ioutil.ReadFile(filename) 303 if err != nil { 304 return nil, err 305 } 306 s := string(b) 307 name := filepath.Base(filename) 308 // First template becomes return value if not already defined, 309 // and we use that one for subsequent New calls to associate 310 // all the templates together. Also, if this file has the same name 311 // as t, this file becomes the contents of t, so 312 // t, err := New(name).Funcs(xxx).ParseFiles(name) 313 // works. Otherwise we create a new template associated with t. 314 var tmpl *Template 315 if t == nil { 316 t = New(name) 317 } 318 if name == t.Name() { 319 tmpl = t 320 } else { 321 tmpl = t.New(name) 322 } 323 _, err = tmpl.Parse(s) 324 if err != nil { 325 return nil, err 326 } 327 } 328 return t, nil 329 } 330 331 // ParseGlob creates a new Template and parses the template definitions from the 332 // files identified by the pattern, which must match at least one file. The 333 // returned template will have the (base) name and (parsed) contents of the 334 // first file matched by the pattern. ParseGlob is equivalent to calling 335 // ParseFiles with the list of files matched by the pattern. 336 func ParseGlob(pattern string) (*Template, error) { 337 return parseGlob(nil, pattern) 338 } 339 340 // ParseGlob parses the template definitions in the files identified by the 341 // pattern and associates the resulting templates with t. The pattern is 342 // processed by filepath.Glob and must match at least one file. ParseGlob is 343 // equivalent to calling t.ParseFiles with the list of files matched by the 344 // pattern. 345 func (t *Template) ParseGlob(pattern string) (*Template, error) { 346 return parseGlob(t, pattern) 347 } 348 349 // parseGlob is the implementation of the function and method ParseGlob. 350 func parseGlob(t *Template, pattern string) (*Template, error) { 351 filenames, err := filepath.Glob(pattern) 352 if err != nil { 353 return nil, err 354 } 355 if len(filenames) == 0 { 356 return nil, fmt.Errorf("html/template: pattern matches no files: %#q", pattern) 357 } 358 return parseFiles(t, filenames...) 359 }