// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package template import ( "fmt" "io" "os" "reflect" "runtime" "strconv" ) // Set holds a set of related templates that can refer to one another by name. // A template may be a member of multiple sets. type Set struct { tmpl map[string]*Template funcs map[string]reflect.Value } // NewSet allocates a new, empty template set. func NewSet() *Set { return &Set{ tmpl: make(map[string]*Template), funcs: make(map[string]reflect.Value), } } // Funcs adds to the set's function map the elements of the // argument map. It panics if a value in the map is not a function // with appropriate return type. // The return value is the set, so calls can be chained. func (s *Set) Funcs(funcMap FuncMap) *Set { addFuncs(s.funcs, funcMap) return s } // Add adds the argument templates to the set. It panics if the call // attempts to reuse a name defined in the template. // The return value is the set, so calls can be chained. func (s *Set) Add(templates ...*Template) *Set { for _, t := range templates { if _, ok := s.tmpl[t.name]; ok { panic(fmt.Errorf("template: %q already defined in set", t.name)) } s.tmpl[t.name] = t } return s } // Template returns the template with the given name in the set, // or nil if there is no such template. func (s *Set) Template(name string) *Template { return s.tmpl[name] } // Execute looks for the named template in the set and then applies that // template to the specified data object, writing the output to wr. Nested // template invocations will be resolved from the set. func (s *Set) Execute(name string, wr io.Writer, data interface{}) os.Error { tmpl := s.tmpl[name] if tmpl == nil { return fmt.Errorf("template: no template %q in set", name) } return tmpl.ExecuteInSet(wr, data, s) } // recover is the handler that turns panics into returns from the top // level of Parse. func (s *Set) recover(errp *os.Error) { e := recover() if e != nil { if _, ok := e.(runtime.Error); ok { panic(e) } s.tmpl = nil *errp = e.(os.Error) } return } // Parse parses the file into a set of named templates. func (s *Set) Parse(text string) (err os.Error) { defer s.recover(&err) lex := lex("set", text) const context = "define clause" for { t := New("set") // name will be updated once we know it. t.startParse(s, lex) // Expect EOF or "{{ define name }}". if t.atEOF() { return } t.expect(itemLeftDelim, context) t.expect(itemDefine, context) name := t.expect(itemString, context) t.name, err = strconv.Unquote(name.val) if err != nil { t.error(err) } t.expect(itemRightDelim, context) end := t.parse(false) if end == nil { t.errorf("unexpected EOF in %s", context) } if end.typ() != nodeEnd { t.errorf("unexpected %s in %s", end, context) } t.stopParse() s.tmpl[t.name] = t } return nil }