OLD | NEW |
1 // Copyright 2011 The Go Authors. All rights reserved. | 1 // Copyright 2011 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 package template | 5 package template |
6 | 6 |
7 import ( | 7 import ( |
8 "bytes" | 8 "bytes" |
| 9 "errors" |
9 "fmt" | 10 "fmt" |
10 "io" | 11 "io" |
11 "net/url" | 12 "net/url" |
12 "reflect" | 13 "reflect" |
13 "strings" | 14 "strings" |
14 "unicode" | 15 "unicode" |
15 "unicode/utf8" | 16 "unicode/utf8" |
16 ) | 17 ) |
17 | 18 |
18 // FuncMap is the type of the map defining the mapping from names to functions. | 19 // 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 // 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 // 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 // return value evaluates to non-nil during execution, execution terminates and |
22 // Execute returns that error. | 23 // Execute returns that error. |
23 type FuncMap map[string]interface{} | 24 type FuncMap map[string]interface{} |
24 | 25 |
25 var builtins = FuncMap{ | 26 var builtins = FuncMap{ |
26 "and": and, | 27 "and": and, |
27 "call": call, | 28 "call": call, |
28 "html": HTMLEscaper, | 29 "html": HTMLEscaper, |
29 "index": index, | 30 "index": index, |
30 "js": JSEscaper, | 31 "js": JSEscaper, |
31 "len": length, | 32 "len": length, |
32 "not": not, | 33 "not": not, |
33 "or": or, | 34 "or": or, |
34 "print": fmt.Sprint, | 35 "print": fmt.Sprint, |
35 "printf": fmt.Sprintf, | 36 "printf": fmt.Sprintf, |
36 "println": fmt.Sprintln, | 37 "println": fmt.Sprintln, |
37 "urlquery": URLQueryEscaper, | 38 "urlquery": URLQueryEscaper, |
| 39 |
| 40 // Comparisons |
| 41 "eq": eq, // == |
| 42 "ge": ge, // >= |
| 43 "gt": gt, // > |
| 44 "le": le, // <= |
| 45 "lt": lt, // < |
| 46 "ne": ne, // != |
38 } | 47 } |
39 | 48 |
40 var builtinFuncs = createValueFuncs(builtins) | 49 var builtinFuncs = createValueFuncs(builtins) |
41 | 50 |
42 // createValueFuncs turns a FuncMap into a map[string]reflect.Value | 51 // createValueFuncs turns a FuncMap into a map[string]reflect.Value |
43 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { | 52 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { |
44 m := make(map[string]reflect.Value) | 53 m := make(map[string]reflect.Value) |
45 addValueFuncs(m, funcMap) | 54 addValueFuncs(m, funcMap) |
46 return m | 55 return m |
47 } | 56 } |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
241 } | 250 } |
242 return arg0 | 251 return arg0 |
243 } | 252 } |
244 | 253 |
245 // not returns the Boolean negation of its argument. | 254 // not returns the Boolean negation of its argument. |
246 func not(arg interface{}) (truth bool) { | 255 func not(arg interface{}) (truth bool) { |
247 truth, _ = isTrue(reflect.ValueOf(arg)) | 256 truth, _ = isTrue(reflect.ValueOf(arg)) |
248 return !truth | 257 return !truth |
249 } | 258 } |
250 | 259 |
| 260 // Comparison. |
| 261 |
| 262 var ( |
| 263 errBadComparisonType = errors.New("invalid type for comparison") |
| 264 errBadComparison = errors.New("incompatible types for comparison") |
| 265 ) |
| 266 |
| 267 type kind int |
| 268 |
| 269 const ( |
| 270 invalidKind kind = iota |
| 271 boolKind |
| 272 complexKind |
| 273 intKind |
| 274 floatKind |
| 275 integerKind |
| 276 stringKind |
| 277 uintKind |
| 278 ) |
| 279 |
| 280 func basicKind(v reflect.Value) (kind, error) { |
| 281 switch v.Kind() { |
| 282 case reflect.Bool: |
| 283 return boolKind, nil |
| 284 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.In
t64: |
| 285 return intKind, nil |
| 286 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflec
t.Uint64, reflect.Uintptr: |
| 287 return uintKind, nil |
| 288 case reflect.Float32, reflect.Float64: |
| 289 return floatKind, nil |
| 290 case reflect.Complex64, reflect.Complex128: |
| 291 return complexKind, nil |
| 292 case reflect.String: |
| 293 return stringKind, nil |
| 294 } |
| 295 return invalidKind, errBadComparisonType |
| 296 } |
| 297 |
| 298 // eq evaluates the comparison a == b. |
| 299 func eq(arg1, arg2 interface{}) (bool, error) { |
| 300 v1 := reflect.ValueOf(arg1) |
| 301 k1, err := basicKind(v1) |
| 302 if err != nil { |
| 303 return false, err |
| 304 } |
| 305 v2 := reflect.ValueOf(arg2) |
| 306 k2, err := basicKind(v2) |
| 307 if err != nil { |
| 308 return false, err |
| 309 } |
| 310 if k1 != k2 { |
| 311 return false, errBadComparison |
| 312 } |
| 313 truth := false |
| 314 switch k1 { |
| 315 case boolKind: |
| 316 truth = v1.Bool() == v2.Bool() |
| 317 case complexKind: |
| 318 truth = v1.Complex() == v2.Complex() |
| 319 case floatKind: |
| 320 truth = v1.Float() == v2.Float() |
| 321 case intKind: |
| 322 truth = v1.Int() == v2.Int() |
| 323 case stringKind: |
| 324 truth = v1.String() == v2.String() |
| 325 case uintKind: |
| 326 truth = v1.Uint() == v2.Uint() |
| 327 default: |
| 328 panic("invalid kind") |
| 329 } |
| 330 return truth, nil |
| 331 } |
| 332 |
| 333 // ne evaluates the comparison a != b. |
| 334 func ne(arg1, arg2 interface{}) (bool, error) { |
| 335 equal, err := eq(arg1, arg2) |
| 336 return !equal, err |
| 337 } |
| 338 |
| 339 // lt evaluates the comparison a < b. |
| 340 func lt(arg1, arg2 interface{}) (bool, error) { |
| 341 v1 := reflect.ValueOf(arg1) |
| 342 v2 := reflect.ValueOf(arg2) |
| 343 k1, err := basicKind(v1) |
| 344 if err != nil { |
| 345 return false, err |
| 346 } |
| 347 k2, err := basicKind(v2) |
| 348 if err != nil { |
| 349 return false, err |
| 350 } |
| 351 if k1 != k2 { |
| 352 return false, errBadComparison |
| 353 } |
| 354 truth := false |
| 355 switch k1 { |
| 356 case boolKind, complexKind: |
| 357 return false, errBadComparisonType |
| 358 case floatKind: |
| 359 truth = v1.Float() < v2.Float() |
| 360 case intKind: |
| 361 truth = v1.Int() < v2.Int() |
| 362 case stringKind: |
| 363 truth = v1.String() < v2.String() |
| 364 case uintKind: |
| 365 truth = v1.Uint() < v2.Uint() |
| 366 default: |
| 367 panic("invalid kind") |
| 368 } |
| 369 return truth, nil |
| 370 } |
| 371 |
| 372 // le evaluates the comparison <= b. |
| 373 func le(arg1, arg2 interface{}) (bool, error) { |
| 374 lessThan, err := lt(arg1, arg2) |
| 375 if lessThan || err != nil { |
| 376 return lessThan, err |
| 377 } |
| 378 return eq(arg1, arg2) |
| 379 } |
| 380 |
| 381 // gt evaluates the comparison a > b. |
| 382 func gt(arg1, arg2 interface{}) (bool, error) { |
| 383 lessThan, err := lt(arg1, arg2) |
| 384 if err != nil { |
| 385 return false, err |
| 386 } |
| 387 return !lessThan, nil |
| 388 } |
| 389 |
| 390 // ge evaluates the comparison a >= b. |
| 391 func ge(arg1, arg2 interface{}) (bool, error) { |
| 392 lessThan, err := lt(arg1, arg2) |
| 393 if err != nil { |
| 394 return false, err |
| 395 } |
| 396 if !lessThan { |
| 397 return true, nil |
| 398 } |
| 399 return eq(arg1, arg2) |
| 400 } |
| 401 |
251 // HTML escaping. | 402 // HTML escaping. |
252 | 403 |
253 var ( | 404 var ( |
254 htmlQuot = []byte(""") // shorter than """ | 405 htmlQuot = []byte(""") // shorter than """ |
255 htmlApos = []byte("'") // shorter than "'" and apos was not in
HTML until HTML5 | 406 htmlApos = []byte("'") // shorter than "'" and apos was not in
HTML until HTML5 |
256 htmlAmp = []byte("&") | 407 htmlAmp = []byte("&") |
257 htmlLt = []byte("<") | 408 htmlLt = []byte("<") |
258 htmlGt = []byte(">") | 409 htmlGt = []byte(">") |
259 ) | 410 ) |
260 | 411 |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
407 func URLQueryEscaper(args ...interface{}) string { | 558 func URLQueryEscaper(args ...interface{}) string { |
408 s, ok := "", false | 559 s, ok := "", false |
409 if len(args) == 1 { | 560 if len(args) == 1 { |
410 s, ok = args[0].(string) | 561 s, ok = args[0].(string) |
411 } | 562 } |
412 if !ok { | 563 if !ok { |
413 s = fmt.Sprint(args...) | 564 s = fmt.Sprint(args...) |
414 } | 565 } |
415 return url.QueryEscape(s) | 566 return url.QueryEscape(s) |
416 } | 567 } |
OLD | NEW |