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(""") // shorter than """ 255 htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 256 htmlAmp = []byte("&") 257 htmlLt = []byte("<") 258 htmlGt = []byte(">") 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 }