diff options
Diffstat (limited to 'libgo/go/html/template/template.go')
-rw-r--r-- | libgo/go/html/template/template.go | 376 |
1 files changed, 200 insertions, 176 deletions
diff --git a/libgo/go/html/template/template.go b/libgo/go/html/template/template.go index 47334299384..f05ca190f73 100644 --- a/libgo/go/html/template/template.go +++ b/libgo/go/html/template/template.go @@ -7,233 +7,257 @@ package template import ( "fmt" "io" + "io/ioutil" "path/filepath" + "sync" "text/template" + "text/template/parse" ) -// Set is a specialized template.Set that produces a safe HTML document -// fragment. -type Set struct { - escaped map[string]bool - template.Set -} - // Template is a specialized template.Template that produces a safe HTML // document fragment. type Template struct { escaped bool - *template.Template + // We could embed the text/template field, but it's safer not to because + // we need to keep our version of the name space and the underlying + // template's in sync. + text *template.Template + *nameSpace // common to all associated templates } -// Execute applies the named template to the specified data object, writing -// the output to wr. -func (s *Set) Execute(wr io.Writer, name string, data interface{}) error { - if !s.escaped[name] { - if err := escapeSet(&s.Set, name); err != nil { - return err - } - if s.escaped == nil { - s.escaped = make(map[string]bool) +// nameSpace is the data structure shared by all templates in an association. +type nameSpace struct { + mu sync.Mutex + set map[string]*Template +} + +// Execute applies a parsed template to the specified data object, +// writing the output to wr. +func (t *Template) Execute(wr io.Writer, data interface{}) (err error) { + t.nameSpace.mu.Lock() + if !t.escaped { + if err = escapeTemplates(t, t.Name()); err != nil { + t.escaped = true } - s.escaped[name] = true } - return s.Set.Execute(wr, name, data) + t.nameSpace.mu.Unlock() + if err != nil { + return + } + return t.text.Execute(wr, data) } -// Parse parses a string into a set of named templates. Parse may be called -// multiple times for a given set, adding the templates defined in the string -// to the set. If a template is redefined, the element in the set is -// overwritten with the new definition. -func (set *Set) Parse(src string) (*Set, error) { - set.escaped = nil - s, err := set.Set.Parse(src) - if err != nil { - return nil, err +// ExecuteTemplate applies the template associated with t that has the given name +// to the specified data object and writes the output to wr. +func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) (err error) { + t.nameSpace.mu.Lock() + tmpl := t.set[name] + if tmpl == nil { + t.nameSpace.mu.Unlock() + return fmt.Errorf("template: no template %q associated with template %q", name, t.Name()) } - if s != &(set.Set) { - panic("allocated new set") + if !tmpl.escaped { + err = escapeTemplates(tmpl, name) + } + t.nameSpace.mu.Unlock() + if err != nil { + return } - return set, nil + return tmpl.text.ExecuteTemplate(wr, name, data) } -// Parse parses the template definition string to construct an internal -// representation of the template for execution. -func (tmpl *Template) Parse(src string) (*Template, error) { - tmpl.escaped = false - t, err := tmpl.Template.Parse(src) +// Parse parses a string into a template. Nested template definitions +// will be associated with the top-level template t. Parse may be +// called multiple times to parse definitions of templates to associate +// with t. It is an error if a resulting template is non-empty (contains +// content other than template definitions) and would replace a +// non-empty template with the same name. (In multiple calls to Parse +// with the same receiver template, only one call can contain text +// other than space, comments, and template definitions.) +func (t *Template) Parse(src string) (*Template, error) { + t.nameSpace.mu.Lock() + t.escaped = false + t.nameSpace.mu.Unlock() + ret, err := t.text.Parse(src) if err != nil { return nil, err } - tmpl.Template = t - return tmpl, nil -} - -// Execute applies a parsed template to the specified data object, -// writing the output to wr. -func (t *Template) Execute(wr io.Writer, data interface{}) error { - if !t.escaped { - if err := escape(t.Template); err != nil { - return err + // In general, all the named templates might have changed underfoot. + // Regardless, some new ones may have been defined. + // The template.Template set has been updated; update ours. + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + for _, v := range ret.Templates() { + name := v.Name() + tmpl := t.set[name] + if tmpl == nil { + tmpl = t.new(name) } - t.escaped = true + tmpl.escaped = false + tmpl.text = v } - return t.Template.Execute(wr, data) + return t, nil +} + +// AddParseTree is unimplemented. +func (t *Template) AddParseTree(name string, tree *parse.Tree) error { + return fmt.Errorf("html/template: AddParseTree unimplemented") +} + +// Clone is unimplemented. +func (t *Template) Clone(name string) error { + return fmt.Errorf("html/template: Add unimplemented") } // New allocates a new HTML template with the given name. func New(name string) *Template { - return &Template{false, template.New(name)} + tmpl := &Template{ + false, + template.New(name), + &nameSpace{ + set: make(map[string]*Template), + }, + } + tmpl.set[name] = tmpl + return tmpl } -// Must panics if err is non-nil in the same way as template.Must. -func Must(t *Template, err error) *Template { - t.Template = template.Must(t.Template, err) - return t +// New allocates a new HTML template associated with the given one +// and with the same delimiters. The association, which is transitive, +// allows one template to invoke another with a {{template}} action. +func (t *Template) New(name string) *Template { + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + return t.new(name) } -// ParseFile creates a new Template and parses the template definition from -// the named file. The template name is the base name of the file. -func ParseFile(filename string) (*Template, error) { - t, err := template.ParseFile(filename) - if err != nil { - return nil, err +// new is the implementation of New, without the lock. +func (t *Template) new(name string) *Template { + tmpl := &Template{ + false, + t.text.New(name), + t.nameSpace, } - return &Template{false, t}, nil + tmpl.set[name] = tmpl + return tmpl } -// ParseFile reads the template definition from a file and parses it to -// construct an internal representation of the template for execution. -// The returned template will be nil if an error occurs. -func (tmpl *Template) ParseFile(filename string) (*Template, error) { - t, err := tmpl.Template.ParseFile(filename) - if err != nil { - return nil, err - } - tmpl.Template = t - return tmpl, nil +// Name returns the name of the template. +func (t *Template) Name() string { + return t.text.Name() } -// SetMust panics if the error is non-nil just like template.SetMust. -func SetMust(s *Set, err error) *Set { - if err != nil { - template.SetMust(&(s.Set), err) - } - return s +// Funcs adds the elements of the argument map to the template's function map. +// It panics if a value in the map is not a function with appropriate return +// type. However, it is legal to overwrite elements of the map. The return +// value is the template, so calls can be chained. +func (t *Template) Funcs(funcMap template.FuncMap) *Template { + t.text.Funcs(funcMap) + return t } -// ParseFiles parses the named files into a set of named templates. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (set *Set) ParseFiles(filenames ...string) (*Set, error) { - s, err := set.Set.ParseFiles(filenames...) - if err != nil { - return nil, err - } - if s != &(set.Set) { - panic("allocated new set") - } - return set, nil +// Delims sets the action delimiters to the specified strings, to be used in +// subsequent calls to Parse, ParseFiles, or ParseGlob. Nested template +// definitions will inherit the settings. An empty delimiter stands for the +// corresponding default: {{ or }}. +// The return value is the template, so calls can be chained. +func (t *Template) Delims(left, right string) *Template { + t.text.Delims(left, right) + return t } -// ParseSetFiles creates a new Set and parses the set definition from the -// named files. Each file must be individually parseable. -func ParseSetFiles(filenames ...string) (*Set, error) { - set := new(Set) - s, err := set.Set.ParseFiles(filenames...) - if err != nil { - return nil, err - } - if s != &(set.Set) { - panic("allocated new set") - } - return set, nil +// Lookup returns the template with the given name that is associated with t, +// or nil if there is no such template. +func (t *Template) Lookup(name string) *Template { + t.nameSpace.mu.Lock() + defer t.nameSpace.mu.Unlock() + return t.set[name] } -// ParseGlob parses the set definition from the files identified by the -// pattern. The pattern is processed by filepath.Glob and must match at -// least one file. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseGlob(pattern string) (*Set, error) { - filenames, err := filepath.Glob(pattern) - if err != nil { - return nil, err - } - if len(filenames) == 0 { - return nil, fmt.Errorf("pattern matches no files: %#q", pattern) - } - return s.ParseFiles(filenames...) +// Must panics if err is non-nil in the same way as template.Must. +func Must(t *Template, err error) *Template { + t.text = template.Must(t.text, err) + return t } -// ParseSetGlob creates a new Set and parses the set definition from the -// files identified by the pattern. The pattern is processed by filepath.Glob -// and must match at least one file. -func ParseSetGlob(pattern string) (*Set, error) { - set, err := new(Set).ParseGlob(pattern) - if err != nil { - return nil, err - } - return set, nil +// ParseFiles creates a new Template and parses the template definitions from +// the named files. The returned template's name will have the (base) name and +// (parsed) contents of the first file. There must be at least one file. +// If an error occurs, parsing stops and the returned *Template is nil. +func ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(nil, filenames...) } -// Functions and methods to parse stand-alone template files into a set. +// ParseFiles parses the named files and associates the resulting templates with +// t. If an error occurs, parsing stops and the returned template is nil; +// otherwise it is t. There must be at least one file. +func (t *Template) ParseFiles(filenames ...string) (*Template, error) { + return parseFiles(t, filenames...) +} -// ParseTemplateFiles parses the named template files and adds them to the set -// in the same way as template.ParseTemplateFiles but ensures that templates -// with upper-case names are contextually-autoescaped. -func (set *Set) ParseTemplateFiles(filenames ...string) (*Set, error) { - s, err := set.Set.ParseTemplateFiles(filenames...) - if err != nil { - return nil, err +// parseFiles is the helper for the method and function. If the argument +// template is nil, it is created from the first file. +func parseFiles(t *Template, filenames ...string) (*Template, error) { + if len(filenames) == 0 { + // Not really a problem, but be consistent. + return nil, fmt.Errorf("template: no files named in call to ParseFiles") } - if s != &(set.Set) { - panic("new set allocated") + for _, filename := range filenames { + b, err := ioutil.ReadFile(filename) + if err != nil { + return nil, err + } + s := string(b) + name := filepath.Base(filename) + // First template becomes return value if not already defined, + // and we use that one for subsequent New calls to associate + // all the templates together. Also, if this file has the same name + // as t, this file becomes the contents of t, so + // t, err := New(name).Funcs(xxx).ParseFiles(name) + // works. Otherwise we create a new template associated with t. + var tmpl *Template + if t == nil { + t = New(name) + } + if name == t.Name() { + tmpl = t + } else { + tmpl = t.New(name) + } + _, err = tmpl.Parse(s) + if err != nil { + return nil, err + } } - return set, nil -} - -// ParseTemplateGlob parses the template files matched by the -// patern and adds them to the set. Each template will be named -// the base name of its file. -// Unlike with ParseGlob, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateGlob is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. -// If an error occurs, parsing stops and the returned set is nil. -func (s *Set) ParseTemplateGlob(pattern string) (*Set, error) { + return t, nil +} + +// ParseGlob creates a new Template and parses the template definitions from the +// files identified by the pattern, which must match at least one file. The +// returned template will have the (base) name and (parsed) contents of the +// first file matched by the pattern. ParseGlob is equivalent to calling +// ParseFiles with the list of files matched by the pattern. +func ParseGlob(pattern string) (*Template, error) { + return parseGlob(nil, pattern) +} + +// ParseGlob parses the template definitions in the files identified by the +// pattern and associates the resulting templates with t. The pattern is +// processed by filepath.Glob and must match at least one file. ParseGlob is +// equivalent to calling t.ParseFiles with the list of files matched by the +// pattern. +func (t *Template) ParseGlob(pattern string) (*Template, error) { + return parseGlob(t, pattern) +} + +// parseGlob is the implementation of the function and method ParseGlob. +func parseGlob(t *Template, pattern string) (*Template, error) { filenames, err := filepath.Glob(pattern) if err != nil { return nil, err } - return s.ParseTemplateFiles(filenames...) -} - -// ParseTemplateFiles creates a set by parsing the named files, -// each of which defines a single template. Each template will be -// named the base name of its file. -// Unlike with ParseFiles, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateFiles is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateFiles(filenames ...string) (*Set, error) { - return new(Set).ParseTemplateFiles(filenames...) -} - -// ParseTemplateGlob creates a set by parsing the files matched -// by the pattern, each of which defines a single template. The pattern -// is processed by filepath.Glob and must match at least one file. Each -// template will be named the base name of its file. -// Unlike with ParseGlob, each file should be a stand-alone template -// definition suitable for Template.Parse (not Set.Parse); that is, the -// file does not contain {{define}} clauses. ParseTemplateGlob is -// therefore equivalent to calling the ParseFile function to create -// individual templates, which are then added to the set. -// Each file must be parseable by itself. Parsing stops if an error is -// encountered. -func ParseTemplateGlob(pattern string) (*Set, error) { - return new(Set).ParseTemplateGlob(pattern) + if len(filenames) == 0 { + return nil, fmt.Errorf("template: pattern matches no files: %#q", pattern) + } + return parseFiles(t, filenames...) } |