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.
18 // FuncMap is the type of the map defining the mapping from names to functions.
19 // Each function must have either a single return value, or two return values of
20 // which the second has type error. If the second argument evaluates to non-nil
21 // during execution, execution terminates and Execute returns an error.
22 type FuncMap map[string]interface{}
24 var builtins = FuncMap{
33 "printf": fmt.Sprintf,
34 "println": fmt.Sprintln,
35 "urlquery": URLQueryEscaper,
38 var builtinFuncs = createValueFuncs(builtins)
40 // createValueFuncs turns a FuncMap into a map[string]reflect.Value
41 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
42 m := make(map[string]reflect.Value)
43 addValueFuncs(m, funcMap)
47 // addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
48 func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
49 for name, fn := range in {
50 v := reflect.ValueOf(fn)
51 if v.Kind() != reflect.Func {
52 panic("value for " + name + " not a function")
54 if !goodFunc(v.Type()) {
55 panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
61 // addFuncs adds to values the functions in funcs. It does no checking of the input -
62 // call addValueFuncs first.
63 func addFuncs(out, in FuncMap) {
64 for name, fn := range in {
69 // goodFunc checks that the function or method has the right result signature.
70 func goodFunc(typ reflect.Type) bool {
71 // We allow functions with 1 result or 2 results where the second is an error.
73 case typ.NumOut() == 1:
75 case typ.NumOut() == 2 && typ.Out(1) == errorType:
81 // findFunction looks for a function in the template, set, and global map.
82 func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
84 if fn := tmpl.execFuncs[name]; fn.IsValid() {
89 if fn := set.execFuncs[name]; fn.IsValid() {
93 if fn := builtinFuncs[name]; fn.IsValid() {
96 return reflect.Value{}, false
101 // index returns the result of indexing its first argument by the following
102 // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
103 // indexed item must be a map, slice, or array.
104 func index(item interface{}, indices ...interface{}) (interface{}, error) {
105 v := reflect.ValueOf(item)
106 for _, i := range indices {
107 index := reflect.ValueOf(i)
109 if v, isNil = indirect(v); isNil {
110 return nil, fmt.Errorf("index of nil pointer")
113 case reflect.Array, reflect.Slice:
115 switch index.Kind() {
116 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
118 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
119 x = int64(index.Uint())
121 return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
123 if x < 0 || x >= int64(v.Len()) {
124 return nil, fmt.Errorf("index out of range: %d", x)
128 if !index.Type().AssignableTo(v.Type().Key()) {
129 return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
131 if x := v.MapIndex(index); x.IsValid() {
134 v = reflect.Zero(v.Type().Key())
137 return nil, fmt.Errorf("can't index item of type %s", index.Type())
140 return v.Interface(), nil
145 // length returns the length of the item, with an error if it has no defined length.
146 func length(item interface{}) (int, error) {
147 v, isNil := indirect(reflect.ValueOf(item))
149 return 0, fmt.Errorf("len of nil pointer")
152 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
155 return 0, fmt.Errorf("len of type %s", v.Type())
160 func truth(a interface{}) bool {
161 t, _ := isTrue(reflect.ValueOf(a))
165 // and computes the Boolean AND of its arguments, returning
166 // the first false argument it encounters, or the last argument.
167 func and(arg0 interface{}, args ...interface{}) interface{} {
171 for i := range args {
180 // or computes the Boolean OR of its arguments, returning
181 // the first true argument it encounters, or the last argument.
182 func or(arg0 interface{}, args ...interface{}) interface{} {
186 for i := range args {
195 // not returns the Boolean negation of its argument.
196 func not(arg interface{}) (truth bool) {
197 truth, _ = isTrue(reflect.ValueOf(arg))
204 htmlQuot = []byte(""") // shorter than """
205 htmlApos = []byte("'") // shorter than "'"
206 htmlAmp = []byte("&")
207 htmlLt = []byte("<")
208 htmlGt = []byte(">")
211 // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
212 func HTMLEscape(w io.Writer, b []byte) {
214 for i, c := range b {
237 // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
238 func HTMLEscapeString(s string) string {
239 // Avoid allocation if we can.
240 if strings.IndexAny(s, `'"&<>`) < 0 {
244 HTMLEscape(&b, []byte(s))
248 // HTMLEscaper returns the escaped HTML equivalent of the textual
249 // representation of its arguments.
250 func HTMLEscaper(args ...interface{}) string {
254 s, ok = args[0].(string)
257 s = fmt.Sprint(args...)
259 return HTMLEscapeString(s)
262 // JavaScript escaping.
265 jsLowUni = []byte(`\u00`)
266 hex = []byte("0123456789ABCDEF")
268 jsBackslash = []byte(`\\`)
269 jsApos = []byte(`\'`)
270 jsQuot = []byte(`\"`)
271 jsLt = []byte(`\x3C`)
272 jsGt = []byte(`\x3E`)
275 // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
276 func JSEscape(w io.Writer, b []byte) {
278 for i := 0; i < len(b); i++ {
281 if !jsIsSpecial(rune(c)) {
282 // fast path: nothing to do
287 if c < utf8.RuneSelf {
288 // Quotes, slashes and angle brackets get quoted.
289 // Control characters get written as \u00XX.
304 w.Write(hex[t : t+1])
305 w.Write(hex[b : b+1])
309 r, size := utf8.DecodeRune(b[i:])
310 if unicode.IsPrint(r) {
311 w.Write(b[i : i+size])
313 // TODO(dsymonds): Do this without fmt?
314 fmt.Fprintf(w, "\\u%04X", r)
323 // JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
324 func JSEscapeString(s string) string {
325 // Avoid allocation if we can.
326 if strings.IndexFunc(s, jsIsSpecial) < 0 {
330 JSEscape(&b, []byte(s))
334 func jsIsSpecial(r rune) bool {
336 case '\\', '\'', '"', '<', '>':
339 return r < ' ' || utf8.RuneSelf <= r
342 // JSEscaper returns the escaped JavaScript equivalent of the textual
343 // representation of its arguments.
344 func JSEscaper(args ...interface{}) string {
348 s, ok = args[0].(string)
351 s = fmt.Sprint(args...)
353 return JSEscapeString(s)
356 // URLQueryEscaper returns the escaped value of the textual representation of
357 // its arguments in a form suitable for embedding in a URL query.
358 func URLQueryEscaper(args ...interface{}) string {
361 s, ok = args[0].(string)
364 s = fmt.Sprint(args...)
366 return url.QueryEscape(s)