github.com/xushiwei/go@v0.0.0-20130601165731-2b9d83f45bc9/src/pkg/text/template/funcs.go (about)

     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  }