src/pkg/text/template/funcs.go - The Go Programming Language

Golang

Source file src/pkg/text/template/funcs.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		"bytes"
     9		"fmt"
    10		"io"
    11		"net/url"
    12		"reflect"
    13		"strings"
    14		"unicode"
    15		"unicode/utf8"
    16	)
    17	
    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. In that case, if the second (error)
    21	// argument evaluates to non-nil during execution, execution terminates and
    22	// Execute returns that error.
    23	type FuncMap map[string]interface{}
    24	
    25	var builtins = FuncMap{
    26		"and":      and,
    27		"call":     call,
    28		"html":     HTMLEscaper,
    29		"index":    index,
    30		"js":       JSEscaper,
    31		"len":      length,
    32		"not":      not,
    33		"or":       or,
    34		"print":    fmt.Sprint,
    35		"printf":   fmt.Sprintf,
    36		"println":  fmt.Sprintln,
    37		"urlquery": URLQueryEscaper,
    38	}
    39	
    40	var builtinFuncs = createValueFuncs(builtins)
    41	
    42	// createValueFuncs turns a FuncMap into a map[string]reflect.Value
    43	func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
    44		m := make(map[string]reflect.Value)
    45		addValueFuncs(m, funcMap)
    46		return m
    47	}
    48	
    49	// addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
    50	func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
    51		for name, fn := range in {
    52			v := reflect.ValueOf(fn)
    53			if v.Kind() != reflect.Func {
    54				panic("value for " + name + " not a function")
    55			}
    56			if !goodFunc(v.Type()) {
    57				panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
    58			}
    59			out[name] = v
    60		}
    61	}
    62	
    63	// addFuncs adds to values the functions in funcs. It does no checking of the input -
    64	// call addValueFuncs first.
    65	func addFuncs(out, in FuncMap) {
    66		for name, fn := range in {
    67			out[name] = fn
    68		}
    69	}
    70	
    71	// goodFunc checks that the function or method has the right result signature.
    72	func goodFunc(typ reflect.Type) bool {
    73		// We allow functions with 1 result or 2 results where the second is an error.
    74		switch {
    75		case typ.NumOut() == 1:
    76			return true
    77		case typ.NumOut() == 2 && typ.Out(1) == errorType:
    78			return true
    79		}
    80		return false
    81	}
    82	
    83	// findFunction looks for a function in the template, and global map.
    84	func findFunction(name string, tmpl *Template) (reflect.Value, bool) {
    85		if tmpl != nil && tmpl.common != nil {
    86			if fn := tmpl.execFuncs[name]; fn.IsValid() {
    87				return fn, true
    88			}
    89		}
    90		if fn := builtinFuncs[name]; fn.IsValid() {
    91			return fn, true
    92		}
    93		return reflect.Value{}, false
    94	}
    95	
    96	// Indexing.
    97	
    98	// index returns the result of indexing its first argument by the following
    99	// arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
   100	// indexed item must be a map, slice, or array.
   101	func index(item interface{}, indices ...interface{}) (interface{}, error) {
   102		v := reflect.ValueOf(item)
   103		for _, i := range indices {
   104			index := reflect.ValueOf(i)
   105			var isNil bool
   106			if v, isNil = indirect(v); isNil {
   107				return nil, fmt.Errorf("index of nil pointer")
   108			}
   109			switch v.Kind() {
   110			case reflect.Array, reflect.Slice:
   111				var x int64
   112				switch index.Kind() {
   113				case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   114					x = index.Int()
   115				case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
   116					x = int64(index.Uint())
   117				default:
   118					return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
   119				}
   120				if x < 0 || x >= int64(v.Len()) {
   121					return nil, fmt.Errorf("index out of range: %d", x)
   122				}
   123				v = v.Index(int(x))
   124			case reflect.Map:
   125				if !index.Type().AssignableTo(v.Type().Key()) {
   126					return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
   127				}
   128				if x := v.MapIndex(index); x.IsValid() {
   129					v = x
   130				} else {
   131					v = reflect.Zero(v.Type().Key())
   132				}
   133			default:
   134				return nil, fmt.Errorf("can't index item of type %s", index.Type())
   135			}
   136		}
   137		return v.Interface(), nil
   138	}
   139	
   140	// Length
   141	
   142	// length returns the length of the item, with an error if it has no defined length.
   143	func length(item interface{}) (int, error) {
   144		v, isNil := indirect(reflect.ValueOf(item))
   145		if isNil {
   146			return 0, fmt.Errorf("len of nil pointer")
   147		}
   148		switch v.Kind() {
   149		case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
   150			return v.Len(), nil
   151		}
   152		return 0, fmt.Errorf("len of type %s", v.Type())
   153	}
   154	
   155	// Function invocation
   156	
   157	// call returns the result of evaluating the the first argument as a function.
   158	// The function must return 1 result, or 2 results, the second of which is an error.
   159	func call(fn interface{}, args ...interface{}) (interface{}, error) {
   160		v := reflect.ValueOf(fn)
   161		typ := v.Type()
   162		if typ.Kind() != reflect.Func {
   163			return nil, fmt.Errorf("non-function of type %s", typ)
   164		}
   165		if !goodFunc(typ) {
   166			return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
   167		}
   168		numIn := typ.NumIn()
   169		var dddType reflect.Type
   170		if typ.IsVariadic() {
   171			if len(args) < numIn-1 {
   172				return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
   173			}
   174			dddType = typ.In(numIn - 1).Elem()
   175		} else {
   176			if len(args) != numIn {
   177				return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
   178			}
   179		}
   180		argv := make([]reflect.Value, len(args))
   181		for i, arg := range args {
   182			value := reflect.ValueOf(arg)
   183			// Compute the expected type. Clumsy because of variadics.
   184			var argType reflect.Type
   185			if !typ.IsVariadic() || i < numIn-1 {
   186				argType = typ.In(i)
   187			} else {
   188				argType = dddType
   189			}
   190			if !value.Type().AssignableTo(argType) {
   191				return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
   192			}
   193			argv[i] = reflect.ValueOf(arg)
   194		}
   195		result := v.Call(argv)
   196		if len(result) == 2 {
   197			return result[0].Interface(), result[1].Interface().(error)
   198		}
   199		return result[0].Interface(), nil
   200	}
   201	
   202	// Boolean logic.
   203	
   204	func truth(a interface{}) bool {
   205		t, _ := isTrue(reflect.ValueOf(a))
   206		return t
   207	}
   208	
   209	// and computes the Boolean AND of its arguments, returning
   210	// the first false argument it encounters, or the last argument.
   211	func and(arg0 interface{}, args ...interface{}) interface{} {
   212		if !truth(arg0) {
   213			return arg0
   214		}
   215		for i := range args {
   216			arg0 = args[i]
   217			if !truth(arg0) {
   218				break
   219			}
   220		}
   221		return arg0
   222	}
   223	
   224	// or computes the Boolean OR of its arguments, returning
   225	// the first true argument it encounters, or the last argument.
   226	func or(arg0 interface{}, args ...interface{}) interface{} {
   227		if truth(arg0) {
   228			return arg0
   229		}
   230		for i := range args {
   231			arg0 = args[i]
   232			if truth(arg0) {
   233				break
   234			}
   235		}
   236		return arg0
   237	}
   238	
   239	// not returns the Boolean negation of its argument.
   240	func not(arg interface{}) (truth bool) {
   241		truth, _ = isTrue(reflect.ValueOf(arg))
   242		return !truth
   243	}
   244	
   245	// HTML escaping.
   246	
   247	var (
   248		htmlQuot = []byte("&#34;") // shorter than "&quot;"
   249		htmlApos = []byte("&#39;") // shorter than "&apos;" and apos was not in HTML until HTML5
   250		htmlAmp  = []byte("&amp;")
   251		htmlLt   = []byte("&lt;")
   252		htmlGt   = []byte("&gt;")
   253	)
   254	
   255	// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
   256	func HTMLEscape(w io.Writer, b []byte) {
   257		last := 0
   258		for i, c := range b {
   259			var html []byte
   260			switch c {
   261			case '"':
   262				html = htmlQuot
   263			case '\'':
   264				html = htmlApos
   265			case '&':
   266				html = htmlAmp
   267			case '<':
   268				html = htmlLt
   269			case '>':
   270				html = htmlGt
   271			default:
   272				continue
   273			}
   274			w.Write(b[last:i])
   275			w.Write(html)
   276			last = i + 1
   277		}
   278		w.Write(b[last:])
   279	}
   280	
   281	// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
   282	func HTMLEscapeString(s string) string {
   283		// Avoid allocation if we can.
   284		if strings.IndexAny(s, `'"&<>`) < 0 {
   285			return s
   286		}
   287		var b bytes.Buffer
   288		HTMLEscape(&b, []byte(s))
   289		return b.String()
   290	}
   291	
   292	// HTMLEscaper returns the escaped HTML equivalent of the textual
   293	// representation of its arguments.
   294	func HTMLEscaper(args ...interface{}) string {
   295		ok := false
   296		var s string
   297		if len(args) == 1 {
   298			s, ok = args[0].(string)
   299		}
   300		if !ok {
   301			s = fmt.Sprint(args...)
   302		}
   303		return HTMLEscapeString(s)
   304	}
   305	
   306	// JavaScript escaping.
   307	
   308	var (
   309		jsLowUni = []byte(`\u00`)
   310		hex      = []byte("0123456789ABCDEF")
   311	
   312		jsBackslash = []byte(`\\`)
   313		jsApos      = []byte(`\'`)
   314		jsQuot      = []byte(`\"`)
   315		jsLt        = []byte(`\x3C`)
   316		jsGt        = []byte(`\x3E`)
   317	)
   318	
   319	// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
   320	func JSEscape(w io.Writer, b []byte) {
   321		last := 0
   322		for i := 0; i < len(b); i++ {
   323			c := b[i]
   324	
   325			if !jsIsSpecial(rune(c)) {
   326				// fast path: nothing to do
   327				continue
   328			}
   329			w.Write(b[last:i])
   330	
   331			if c < utf8.RuneSelf {
   332				// Quotes, slashes and angle brackets get quoted.
   333				// Control characters get written as \u00XX.
   334				switch c {
   335				case '\\':
   336					w.Write(jsBackslash)
   337				case '\'':
   338					w.Write(jsApos)
   339				case '"':
   340					w.Write(jsQuot)
   341				case '<':
   342					w.Write(jsLt)
   343				case '>':
   344					w.Write(jsGt)
   345				default:
   346					w.Write(jsLowUni)
   347					t, b := c>>4, c&0x0f
   348					w.Write(hex[t : t+1])
   349					w.Write(hex[b : b+1])
   350				}
   351			} else {
   352				// Unicode rune.
   353				r, size := utf8.DecodeRune(b[i:])
   354				if unicode.IsPrint(r) {
   355					w.Write(b[i : i+size])
   356				} else {
   357					fmt.Fprintf(w, "\\u%04X", r)
   358				}
   359				i += size - 1
   360			}
   361			last = i + 1
   362		}
   363		w.Write(b[last:])
   364	}
   365	
   366	// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
   367	func JSEscapeString(s string) string {
   368		// Avoid allocation if we can.
   369		if strings.IndexFunc(s, jsIsSpecial) < 0 {
   370			return s
   371		}
   372		var b bytes.Buffer
   373		JSEscape(&b, []byte(s))
   374		return b.String()
   375	}
   376	
   377	func jsIsSpecial(r rune) bool {
   378		switch r {
   379		case '\\', '\'', '"', '<', '>':
   380			return true
   381		}
   382		return r < ' ' || utf8.RuneSelf <= r
   383	}
   384	
   385	// JSEscaper returns the escaped JavaScript equivalent of the textual
   386	// representation of its arguments.
   387	func JSEscaper(args ...interface{}) string {
   388		ok := false
   389		var s string
   390		if len(args) == 1 {
   391			s, ok = args[0].(string)
   392		}
   393		if !ok {
   394			s = fmt.Sprint(args...)
   395		}
   396		return JSEscapeString(s)
   397	}
   398	
   399	// URLQueryEscaper returns the escaped value of the textual representation of
   400	// its arguments in a form suitable for embedding in a URL query.
   401	func URLQueryEscaper(args ...interface{}) string {
   402		s, ok := "", false
   403		if len(args) == 1 {
   404			s, ok = args[0].(string)
   405		}
   406		if !ok {
   407			s = fmt.Sprint(args...)
   408		}
   409		return url.QueryEscape(s)
   410	}