| // 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 ( | 
 | 	"bytes" | 
 | 	"fmt" | 
 | 	"io" | 
 | 	"net/url" | 
 | 	"reflect" | 
 | 	"strings" | 
 | 	"unicode" | 
 | 	"unicode/utf8" | 
 | ) | 
 |  | 
 | // FuncMap is the type of the map defining the mapping from names to functions. | 
 | // Each function must have either a single return value, or two return values of | 
 | // which the second has type error. In that case, if the second (error) | 
 | // argument evaluates to non-nil during execution, execution terminates and | 
 | // Execute returns that error. | 
 | type FuncMap map[string]interface{} | 
 |  | 
 | var builtins = FuncMap{ | 
 | 	"and":      and, | 
 | 	"call":     call, | 
 | 	"html":     HTMLEscaper, | 
 | 	"index":    index, | 
 | 	"js":       JSEscaper, | 
 | 	"len":      length, | 
 | 	"not":      not, | 
 | 	"or":       or, | 
 | 	"print":    fmt.Sprint, | 
 | 	"printf":   fmt.Sprintf, | 
 | 	"println":  fmt.Sprintln, | 
 | 	"urlquery": URLQueryEscaper, | 
 | } | 
 |  | 
 | var builtinFuncs = createValueFuncs(builtins) | 
 |  | 
 | // createValueFuncs turns a FuncMap into a map[string]reflect.Value | 
 | func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { | 
 | 	m := make(map[string]reflect.Value) | 
 | 	addValueFuncs(m, funcMap) | 
 | 	return m | 
 | } | 
 |  | 
 | // addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. | 
 | func addValueFuncs(out map[string]reflect.Value, in FuncMap) { | 
 | 	for name, fn := range in { | 
 | 		v := reflect.ValueOf(fn) | 
 | 		if v.Kind() != reflect.Func { | 
 | 			panic("value for " + name + " not a function") | 
 | 		} | 
 | 		if !goodFunc(v.Type()) { | 
 | 			panic(fmt.Errorf("can't handle multiple results from method/function %q", name)) | 
 | 		} | 
 | 		out[name] = v | 
 | 	} | 
 | } | 
 |  | 
 | // addFuncs adds to values the functions in funcs. It does no checking of the input - | 
 | // call addValueFuncs first. | 
 | func addFuncs(out, in FuncMap) { | 
 | 	for name, fn := range in { | 
 | 		out[name] = fn | 
 | 	} | 
 | } | 
 |  | 
 | // goodFunc checks that the function or method has the right result signature. | 
 | func goodFunc(typ reflect.Type) bool { | 
 | 	// We allow functions with 1 result or 2 results where the second is an error. | 
 | 	switch { | 
 | 	case typ.NumOut() == 1: | 
 | 		return true | 
 | 	case typ.NumOut() == 2 && typ.Out(1) == errorType: | 
 | 		return true | 
 | 	} | 
 | 	return false | 
 | } | 
 |  | 
 | // findFunction looks for a function in the template, and global map. | 
 | func findFunction(name string, tmpl *Template) (reflect.Value, bool) { | 
 | 	if tmpl != nil && tmpl.common != nil { | 
 | 		if fn := tmpl.execFuncs[name]; fn.IsValid() { | 
 | 			return fn, true | 
 | 		} | 
 | 	} | 
 | 	if fn := builtinFuncs[name]; fn.IsValid() { | 
 | 		return fn, true | 
 | 	} | 
 | 	return reflect.Value{}, false | 
 | } | 
 |  | 
 | // Indexing. | 
 |  | 
 | // index returns the result of indexing its first argument by the following | 
 | // arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each | 
 | // indexed item must be a map, slice, or array. | 
 | func index(item interface{}, indices ...interface{}) (interface{}, error) { | 
 | 	v := reflect.ValueOf(item) | 
 | 	for _, i := range indices { | 
 | 		index := reflect.ValueOf(i) | 
 | 		var isNil bool | 
 | 		if v, isNil = indirect(v); isNil { | 
 | 			return nil, fmt.Errorf("index of nil pointer") | 
 | 		} | 
 | 		switch v.Kind() { | 
 | 		case reflect.Array, reflect.Slice: | 
 | 			var x int64 | 
 | 			switch index.Kind() { | 
 | 			case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | 
 | 				x = index.Int() | 
 | 			case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | 
 | 				x = int64(index.Uint()) | 
 | 			default: | 
 | 				return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type()) | 
 | 			} | 
 | 			if x < 0 || x >= int64(v.Len()) { | 
 | 				return nil, fmt.Errorf("index out of range: %d", x) | 
 | 			} | 
 | 			v = v.Index(int(x)) | 
 | 		case reflect.Map: | 
 | 			if !index.Type().AssignableTo(v.Type().Key()) { | 
 | 				return nil, fmt.Errorf("%s is not index type for %s", index.Type(), v.Type()) | 
 | 			} | 
 | 			if x := v.MapIndex(index); x.IsValid() { | 
 | 				v = x | 
 | 			} else { | 
 | 				v = reflect.Zero(v.Type().Elem()) | 
 | 			} | 
 | 		default: | 
 | 			return nil, fmt.Errorf("can't index item of type %s", index.Type()) | 
 | 		} | 
 | 	} | 
 | 	return v.Interface(), nil | 
 | } | 
 |  | 
 | // Length | 
 |  | 
 | // length returns the length of the item, with an error if it has no defined length. | 
 | func length(item interface{}) (int, error) { | 
 | 	v, isNil := indirect(reflect.ValueOf(item)) | 
 | 	if isNil { | 
 | 		return 0, fmt.Errorf("len of nil pointer") | 
 | 	} | 
 | 	switch v.Kind() { | 
 | 	case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: | 
 | 		return v.Len(), nil | 
 | 	} | 
 | 	return 0, fmt.Errorf("len of type %s", v.Type()) | 
 | } | 
 |  | 
 | // Function invocation | 
 |  | 
 | // call returns the result of evaluating the first argument as a function. | 
 | // The function must return 1 result, or 2 results, the second of which is an error. | 
 | func call(fn interface{}, args ...interface{}) (interface{}, error) { | 
 | 	v := reflect.ValueOf(fn) | 
 | 	typ := v.Type() | 
 | 	if typ.Kind() != reflect.Func { | 
 | 		return nil, fmt.Errorf("non-function of type %s", typ) | 
 | 	} | 
 | 	if !goodFunc(typ) { | 
 | 		return nil, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) | 
 | 	} | 
 | 	numIn := typ.NumIn() | 
 | 	var dddType reflect.Type | 
 | 	if typ.IsVariadic() { | 
 | 		if len(args) < numIn-1 { | 
 | 			return nil, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) | 
 | 		} | 
 | 		dddType = typ.In(numIn - 1).Elem() | 
 | 	} else { | 
 | 		if len(args) != numIn { | 
 | 			return nil, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) | 
 | 		} | 
 | 	} | 
 | 	argv := make([]reflect.Value, len(args)) | 
 | 	for i, arg := range args { | 
 | 		value := reflect.ValueOf(arg) | 
 | 		// Compute the expected type. Clumsy because of variadics. | 
 | 		var argType reflect.Type | 
 | 		if !typ.IsVariadic() || i < numIn-1 { | 
 | 			argType = typ.In(i) | 
 | 		} else { | 
 | 			argType = dddType | 
 | 		} | 
 | 		if !value.Type().AssignableTo(argType) { | 
 | 			return nil, fmt.Errorf("arg %d has type %s; should be %s", i, value.Type(), argType) | 
 | 		} | 
 | 		argv[i] = reflect.ValueOf(arg) | 
 | 	} | 
 | 	result := v.Call(argv) | 
 | 	if len(result) == 2 { | 
 | 		return result[0].Interface(), result[1].Interface().(error) | 
 | 	} | 
 | 	return result[0].Interface(), nil | 
 | } | 
 |  | 
 | // Boolean logic. | 
 |  | 
 | func truth(a interface{}) bool { | 
 | 	t, _ := isTrue(reflect.ValueOf(a)) | 
 | 	return t | 
 | } | 
 |  | 
 | // and computes the Boolean AND of its arguments, returning | 
 | // the first false argument it encounters, or the last argument. | 
 | func and(arg0 interface{}, args ...interface{}) interface{} { | 
 | 	if !truth(arg0) { | 
 | 		return arg0 | 
 | 	} | 
 | 	for i := range args { | 
 | 		arg0 = args[i] | 
 | 		if !truth(arg0) { | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	return arg0 | 
 | } | 
 |  | 
 | // or computes the Boolean OR of its arguments, returning | 
 | // the first true argument it encounters, or the last argument. | 
 | func or(arg0 interface{}, args ...interface{}) interface{} { | 
 | 	if truth(arg0) { | 
 | 		return arg0 | 
 | 	} | 
 | 	for i := range args { | 
 | 		arg0 = args[i] | 
 | 		if truth(arg0) { | 
 | 			break | 
 | 		} | 
 | 	} | 
 | 	return arg0 | 
 | } | 
 |  | 
 | // not returns the Boolean negation of its argument. | 
 | func not(arg interface{}) (truth bool) { | 
 | 	truth, _ = isTrue(reflect.ValueOf(arg)) | 
 | 	return !truth | 
 | } | 
 |  | 
 | // HTML escaping. | 
 |  | 
 | var ( | 
 | 	htmlQuot = []byte(""") // shorter than """ | 
 | 	htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 | 
 | 	htmlAmp  = []byte("&") | 
 | 	htmlLt   = []byte("<") | 
 | 	htmlGt   = []byte(">") | 
 | ) | 
 |  | 
 | // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. | 
 | func HTMLEscape(w io.Writer, b []byte) { | 
 | 	last := 0 | 
 | 	for i, c := range b { | 
 | 		var html []byte | 
 | 		switch c { | 
 | 		case '"': | 
 | 			html = htmlQuot | 
 | 		case '\'': | 
 | 			html = htmlApos | 
 | 		case '&': | 
 | 			html = htmlAmp | 
 | 		case '<': | 
 | 			html = htmlLt | 
 | 		case '>': | 
 | 			html = htmlGt | 
 | 		default: | 
 | 			continue | 
 | 		} | 
 | 		w.Write(b[last:i]) | 
 | 		w.Write(html) | 
 | 		last = i + 1 | 
 | 	} | 
 | 	w.Write(b[last:]) | 
 | } | 
 |  | 
 | // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. | 
 | func HTMLEscapeString(s string) string { | 
 | 	// Avoid allocation if we can. | 
 | 	if strings.IndexAny(s, `'"&<>`) < 0 { | 
 | 		return s | 
 | 	} | 
 | 	var b bytes.Buffer | 
 | 	HTMLEscape(&b, []byte(s)) | 
 | 	return b.String() | 
 | } | 
 |  | 
 | // HTMLEscaper returns the escaped HTML equivalent of the textual | 
 | // representation of its arguments. | 
 | func HTMLEscaper(args ...interface{}) string { | 
 | 	ok := false | 
 | 	var s string | 
 | 	if len(args) == 1 { | 
 | 		s, ok = args[0].(string) | 
 | 	} | 
 | 	if !ok { | 
 | 		s = fmt.Sprint(args...) | 
 | 	} | 
 | 	return HTMLEscapeString(s) | 
 | } | 
 |  | 
 | // JavaScript escaping. | 
 |  | 
 | var ( | 
 | 	jsLowUni = []byte(`\u00`) | 
 | 	hex      = []byte("0123456789ABCDEF") | 
 |  | 
 | 	jsBackslash = []byte(`\\`) | 
 | 	jsApos      = []byte(`\'`) | 
 | 	jsQuot      = []byte(`\"`) | 
 | 	jsLt        = []byte(`\x3C`) | 
 | 	jsGt        = []byte(`\x3E`) | 
 | ) | 
 |  | 
 | // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. | 
 | func JSEscape(w io.Writer, b []byte) { | 
 | 	last := 0 | 
 | 	for i := 0; i < len(b); i++ { | 
 | 		c := b[i] | 
 |  | 
 | 		if !jsIsSpecial(rune(c)) { | 
 | 			// fast path: nothing to do | 
 | 			continue | 
 | 		} | 
 | 		w.Write(b[last:i]) | 
 |  | 
 | 		if c < utf8.RuneSelf { | 
 | 			// Quotes, slashes and angle brackets get quoted. | 
 | 			// Control characters get written as \u00XX. | 
 | 			switch c { | 
 | 			case '\\': | 
 | 				w.Write(jsBackslash) | 
 | 			case '\'': | 
 | 				w.Write(jsApos) | 
 | 			case '"': | 
 | 				w.Write(jsQuot) | 
 | 			case '<': | 
 | 				w.Write(jsLt) | 
 | 			case '>': | 
 | 				w.Write(jsGt) | 
 | 			default: | 
 | 				w.Write(jsLowUni) | 
 | 				t, b := c>>4, c&0x0f | 
 | 				w.Write(hex[t : t+1]) | 
 | 				w.Write(hex[b : b+1]) | 
 | 			} | 
 | 		} else { | 
 | 			// Unicode rune. | 
 | 			r, size := utf8.DecodeRune(b[i:]) | 
 | 			if unicode.IsPrint(r) { | 
 | 				w.Write(b[i : i+size]) | 
 | 			} else { | 
 | 				fmt.Fprintf(w, "\\u%04X", r) | 
 | 			} | 
 | 			i += size - 1 | 
 | 		} | 
 | 		last = i + 1 | 
 | 	} | 
 | 	w.Write(b[last:]) | 
 | } | 
 |  | 
 | // JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. | 
 | func JSEscapeString(s string) string { | 
 | 	// Avoid allocation if we can. | 
 | 	if strings.IndexFunc(s, jsIsSpecial) < 0 { | 
 | 		return s | 
 | 	} | 
 | 	var b bytes.Buffer | 
 | 	JSEscape(&b, []byte(s)) | 
 | 	return b.String() | 
 | } | 
 |  | 
 | func jsIsSpecial(r rune) bool { | 
 | 	switch r { | 
 | 	case '\\', '\'', '"', '<', '>': | 
 | 		return true | 
 | 	} | 
 | 	return r < ' ' || utf8.RuneSelf <= r | 
 | } | 
 |  | 
 | // JSEscaper returns the escaped JavaScript equivalent of the textual | 
 | // representation of its arguments. | 
 | func JSEscaper(args ...interface{}) string { | 
 | 	ok := false | 
 | 	var s string | 
 | 	if len(args) == 1 { | 
 | 		s, ok = args[0].(string) | 
 | 	} | 
 | 	if !ok { | 
 | 		s = fmt.Sprint(args...) | 
 | 	} | 
 | 	return JSEscapeString(s) | 
 | } | 
 |  | 
 | // URLQueryEscaper returns the escaped value of the textual representation of | 
 | // its arguments in a form suitable for embedding in a URL query. | 
 | func URLQueryEscaper(args ...interface{}) string { | 
 | 	s, ok := "", false | 
 | 	if len(args) == 1 { | 
 | 		s, ok = args[0].(string) | 
 | 	} | 
 | 	if !ok { | 
 | 		s = fmt.Sprint(args...) | 
 | 	} | 
 | 	return url.QueryEscape(s) | 
 | } |