Run Format

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	// return value 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 install method/function %q with %d results", name, v.Type().NumOut()))
    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, reflect.String:
   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.IsValid() {
   126					index = reflect.Zero(v.Type().Key())
   127				}
   128				if !index.Type().AssignableTo(v.Type().Key()) {
   129					return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type())
   130				}
   131				if x := v.MapIndex(index); x.IsValid() {
   132					v = x
   133				} else {
   134					v = reflect.Zero(v.Type().Elem())
   135				}
   136			default:
   137				return nil, fmt.Errorf("can't index item of type %s", v.Type())
   138			}
   139		}
   140		return v.Interface(), nil
   141	}
   142	
   143	// Length
   144	
   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))
   148		if isNil {
   149			return 0, fmt.Errorf("len of nil pointer")
   150		}
   151		switch v.Kind() {
   152		case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String:
   153			return v.Len(), nil
   154		}
   155		return 0, fmt.Errorf("len of type %s", v.Type())
   156	}
   157	
   158	// Function invocation
   159	
   160	// call returns the result of evaluating the first argument as a function.
   161	// The function must return 1 result, or 2 results, the second of which is an error.
   162	func call(fn interface{}, args ...interface{}) (interface{}, error) {
   163		v := reflect.ValueOf(fn)
   164		typ := v.Type()
   165		if typ.Kind() != reflect.Func {
   166			return nil, fmt.Errorf("non-function of type %s", typ)
   167		}
   168		if !goodFunc(typ) {
   169			return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut())
   170		}
   171		numIn := typ.NumIn()
   172		var dddType reflect.Type
   173		if typ.IsVariadic() {
   174			if len(args) < numIn-1 {
   175				return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1)
   176			}
   177			dddType = typ.In(numIn - 1).Elem()
   178		} else {
   179			if len(args) != numIn {
   180				return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn)
   181			}
   182		}
   183		argv := make([]reflect.Value, len(args))
   184		for i, arg := range args {
   185			value := reflect.ValueOf(arg)
   186			// Compute the expected type. Clumsy because of variadics.
   187			var argType reflect.Type
   188			if !typ.IsVariadic() || i < numIn-1 {
   189				argType = typ.In(i)
   190			} else {
   191				argType = dddType
   192			}
   193			if !value.IsValid() && canBeNil(argType) {
   194				value = reflect.Zero(argType)
   195			}
   196			if !value.Type().AssignableTo(argType) {
   197				return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType)
   198			}
   199			argv[i] = value
   200		}
   201		result := v.Call(argv)
   202		if len(result) == 2 {
   203			return result[0].Interface(), result[1].Interface().(error)
   204		}
   205		return result[0].Interface(), nil
   206	}
   207	
   208	// Boolean logic.
   209	
   210	func truth(a interface{}) bool {
   211		t, _ := isTrue(reflect.ValueOf(a))
   212		return t
   213	}
   214	
   215	// and computes the Boolean AND of its arguments, returning
   216	// the first false argument it encounters, or the last argument.
   217	func and(arg0 interface{}, args ...interface{}) interface{} {
   218		if !truth(arg0) {
   219			return arg0
   220		}
   221		for i := range args {
   222			arg0 = args[i]
   223			if !truth(arg0) {
   224				break
   225			}
   226		}
   227		return arg0
   228	}
   229	
   230	// or computes the Boolean OR of its arguments, returning
   231	// the first true argument it encounters, or the last argument.
   232	func or(arg0 interface{}, args ...interface{}) interface{} {
   233		if truth(arg0) {
   234			return arg0
   235		}
   236		for i := range args {
   237			arg0 = args[i]
   238			if truth(arg0) {
   239				break
   240			}
   241		}
   242		return arg0
   243	}
   244	
   245	// not returns the Boolean negation of its argument.
   246	func not(arg interface{}) (truth bool) {
   247		truth, _ = isTrue(reflect.ValueOf(arg))
   248		return !truth
   249	}
   250	
   251	// HTML escaping.
   252	
   253	var (
   254		htmlQuot = []byte("&#34;") // shorter than "&quot;"
   255		htmlApos = []byte("&#39;") // shorter than "&apos;" and apos was not in HTML until HTML5
   256		htmlAmp  = []byte("&amp;")
   257		htmlLt   = []byte("&lt;")
   258		htmlGt   = []byte("&gt;")
   259	)
   260	
   261	// HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
   262	func HTMLEscape(w io.Writer, b []byte) {
   263		last := 0
   264		for i, c := range b {
   265			var html []byte
   266			switch c {
   267			case '"':
   268				html = htmlQuot
   269			case '\'':
   270				html = htmlApos
   271			case '&':
   272				html = htmlAmp
   273			case '<':
   274				html = htmlLt
   275			case '>':
   276				html = htmlGt
   277			default:
   278				continue
   279			}
   280			w.Write(b[last:i])
   281			w.Write(html)
   282			last = i + 1
   283		}
   284		w.Write(b[last:])
   285	}
   286	
   287	// HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
   288	func HTMLEscapeString(s string) string {
   289		// Avoid allocation if we can.
   290		if strings.IndexAny(s, `'"&<>`) < 0 {
   291			return s
   292		}
   293		var b bytes.Buffer
   294		HTMLEscape(&b, []byte(s))
   295		return b.String()
   296	}
   297	
   298	// HTMLEscaper returns the escaped HTML equivalent of the textual
   299	// representation of its arguments.
   300	func HTMLEscaper(args ...interface{}) string {
   301		ok := false
   302		var s string
   303		if len(args) == 1 {
   304			s, ok = args[0].(string)
   305		}
   306		if !ok {
   307			s = fmt.Sprint(args...)
   308		}
   309		return HTMLEscapeString(s)
   310	}
   311	
   312	// JavaScript escaping.
   313	
   314	var (
   315		jsLowUni = []byte(`\u00`)
   316		hex      = []byte("0123456789ABCDEF")
   317	
   318		jsBackslash = []byte(`\\`)
   319		jsApos      = []byte(`\'`)
   320		jsQuot      = []byte(`\"`)
   321		jsLt        = []byte(`\x3C`)
   322		jsGt        = []byte(`\x3E`)
   323	)
   324	
   325	// JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
   326	func JSEscape(w io.Writer, b []byte) {
   327		last := 0
   328		for i := 0; i < len(b); i++ {
   329			c := b[i]
   330	
   331			if !jsIsSpecial(rune(c)) {
   332				// fast path: nothing to do
   333				continue
   334			}
   335			w.Write(b[last:i])
   336	
   337			if c < utf8.RuneSelf {
   338				// Quotes, slashes and angle brackets get quoted.
   339				// Control characters get written as \u00XX.
   340				switch c {
   341				case '\\':
   342					w.Write(jsBackslash)
   343				case '\'':
   344					w.Write(jsApos)
   345				case '"':
   346					w.Write(jsQuot)
   347				case '<':
   348					w.Write(jsLt)
   349				case '>':
   350					w.Write(jsGt)
   351				default:
   352					w.Write(jsLowUni)
   353					t, b := c>>4, c&0x0f
   354					w.Write(hex[t : t+1])
   355					w.Write(hex[b : b+1])
   356				}
   357			} else {
   358				// Unicode rune.
   359				r, size := utf8.DecodeRune(b[i:])
   360				if unicode.IsPrint(r) {
   361					w.Write(b[i : i+size])
   362				} else {
   363					fmt.Fprintf(w, "\\u%04X", r)
   364				}
   365				i += size - 1
   366			}
   367			last = i + 1
   368		}
   369		w.Write(b[last:])
   370	}
   371	
   372	// JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
   373	func JSEscapeString(s string) string {
   374		// Avoid allocation if we can.
   375		if strings.IndexFunc(s, jsIsSpecial) < 0 {
   376			return s
   377		}
   378		var b bytes.Buffer
   379		JSEscape(&b, []byte(s))
   380		return b.String()
   381	}
   382	
   383	func jsIsSpecial(r rune) bool {
   384		switch r {
   385		case '\\', '\'', '"', '<', '>':
   386			return true
   387		}
   388		return r < ' ' || utf8.RuneSelf <= r
   389	}
   390	
   391	// JSEscaper returns the escaped JavaScript equivalent of the textual
   392	// representation of its arguments.
   393	func JSEscaper(args ...interface{}) string {
   394		ok := false
   395		var s string
   396		if len(args) == 1 {
   397			s, ok = args[0].(string)
   398		}
   399		if !ok {
   400			s = fmt.Sprint(args...)
   401		}
   402		return JSEscapeString(s)
   403	}
   404	
   405	// URLQueryEscaper returns the escaped value of the textual representation of
   406	// its arguments in a form suitable for embedding in a URL query.
   407	func URLQueryEscaper(args ...interface{}) string {
   408		s, ok := "", false
   409		if len(args) == 1 {
   410			s, ok = args[0].(string)
   411		}
   412		if !ok {
   413			s = fmt.Sprint(args...)
   414		}
   415		return url.QueryEscape(s)
   416	}

View as plain text