OSDN Git Service

libgo: Update to weekly 2011-11-09.
[pf3gnuchains/gcc-fork.git] / libgo / go / 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. If the second argument evaluates to non-nil
21 // during execution, execution terminates and Execute returns an error.
22 type FuncMap map[string]interface{}
23
24 var builtins = FuncMap{
25         "and":      and,
26         "html":     HTMLEscaper,
27         "index":    index,
28         "js":       JSEscaper,
29         "len":      length,
30         "not":      not,
31         "or":       or,
32         "print":    fmt.Sprint,
33         "printf":   fmt.Sprintf,
34         "println":  fmt.Sprintln,
35         "urlquery": URLQueryEscaper,
36 }
37
38 var builtinFuncs = createValueFuncs(builtins)
39
40 // createValueFuncs turns a FuncMap into a map[string]reflect.Value
41 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value {
42         m := make(map[string]reflect.Value)
43         addValueFuncs(m, funcMap)
44         return m
45 }
46
47 // addValueFuncs adds to values the functions in funcs, converting them to reflect.Values.
48 func addValueFuncs(out map[string]reflect.Value, in FuncMap) {
49         for name, fn := range in {
50                 v := reflect.ValueOf(fn)
51                 if v.Kind() != reflect.Func {
52                         panic("value for " + name + " not a function")
53                 }
54                 if !goodFunc(v.Type()) {
55                         panic(fmt.Errorf("can't handle multiple results from method/function %q", name))
56                 }
57                 out[name] = v
58         }
59 }
60
61 // addFuncs adds to values the functions in funcs. It does no checking of the input -
62 // call addValueFuncs first.
63 func addFuncs(out, in FuncMap) {
64         for name, fn := range in {
65                 out[name] = fn
66         }
67 }
68
69 // goodFunc checks that the function or method has the right result signature.
70 func goodFunc(typ reflect.Type) bool {
71         // We allow functions with 1 result or 2 results where the second is an error.
72         switch {
73         case typ.NumOut() == 1:
74                 return true
75         case typ.NumOut() == 2 && typ.Out(1) == errorType:
76                 return true
77         }
78         return false
79 }
80
81 // findFunction looks for a function in the template, set, and global map.
82 func findFunction(name string, tmpl *Template, set *Set) (reflect.Value, bool) {
83         if tmpl != nil {
84                 if fn := tmpl.execFuncs[name]; fn.IsValid() {
85                         return fn, true
86                 }
87         }
88         if set != nil {
89                 if fn := set.execFuncs[name]; fn.IsValid() {
90                         return fn, true
91                 }
92         }
93         if fn := builtinFuncs[name]; fn.IsValid() {
94                 return fn, true
95         }
96         return reflect.Value{}, false
97 }
98
99 // Indexing.
100
101 // index returns the result of indexing its first argument by the following
102 // arguments.  Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each
103 // indexed item must be a map, slice, or array.
104 func index(item interface{}, indices ...interface{}) (interface{}, error) {
105         v := reflect.ValueOf(item)
106         for _, i := range indices {
107                 index := reflect.ValueOf(i)
108                 var isNil bool
109                 if v, isNil = indirect(v); isNil {
110                         return nil, fmt.Errorf("index of nil pointer")
111                 }
112                 switch v.Kind() {
113                 case reflect.Array, reflect.Slice:
114                         var x int64
115                         switch index.Kind() {
116                         case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
117                                 x = index.Int()
118                         case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
119                                 x = int64(index.Uint())
120                         default:
121                                 return nil, fmt.Errorf("cannot index slice/array with type %s", index.Type())
122                         }
123                         if x < 0 || x >= int64(v.Len()) {
124                                 return nil, fmt.Errorf("index out of range: %d", x)
125                         }
126                         v = v.Index(int(x))
127                 case reflect.Map:
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().Key())
135                         }
136                 default:
137                         return nil, fmt.Errorf("can't index item of type %s", index.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 // Boolean logic.
159
160 func truth(a interface{}) bool {
161         t, _ := isTrue(reflect.ValueOf(a))
162         return t
163 }
164
165 // and computes the Boolean AND of its arguments, returning
166 // the first false argument it encounters, or the last argument.
167 func and(arg0 interface{}, args ...interface{}) interface{} {
168         if !truth(arg0) {
169                 return arg0
170         }
171         for i := range args {
172                 arg0 = args[i]
173                 if !truth(arg0) {
174                         break
175                 }
176         }
177         return arg0
178 }
179
180 // or computes the Boolean OR of its arguments, returning
181 // the first true argument it encounters, or the last argument.
182 func or(arg0 interface{}, args ...interface{}) interface{} {
183         if truth(arg0) {
184                 return arg0
185         }
186         for i := range args {
187                 arg0 = args[i]
188                 if truth(arg0) {
189                         break
190                 }
191         }
192         return arg0
193 }
194
195 // not returns the Boolean negation of its argument.
196 func not(arg interface{}) (truth bool) {
197         truth, _ = isTrue(reflect.ValueOf(arg))
198         return !truth
199 }
200
201 // HTML escaping.
202
203 var (
204         htmlQuot = []byte("&#34;") // shorter than "&quot;"
205         htmlApos = []byte("&#39;") // shorter than "&apos;"
206         htmlAmp  = []byte("&amp;")
207         htmlLt   = []byte("&lt;")
208         htmlGt   = []byte("&gt;")
209 )
210
211 // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b.
212 func HTMLEscape(w io.Writer, b []byte) {
213         last := 0
214         for i, c := range b {
215                 var html []byte
216                 switch c {
217                 case '"':
218                         html = htmlQuot
219                 case '\'':
220                         html = htmlApos
221                 case '&':
222                         html = htmlAmp
223                 case '<':
224                         html = htmlLt
225                 case '>':
226                         html = htmlGt
227                 default:
228                         continue
229                 }
230                 w.Write(b[last:i])
231                 w.Write(html)
232                 last = i + 1
233         }
234         w.Write(b[last:])
235 }
236
237 // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s.
238 func HTMLEscapeString(s string) string {
239         // Avoid allocation if we can.
240         if strings.IndexAny(s, `'"&<>`) < 0 {
241                 return s
242         }
243         var b bytes.Buffer
244         HTMLEscape(&b, []byte(s))
245         return b.String()
246 }
247
248 // HTMLEscaper returns the escaped HTML equivalent of the textual
249 // representation of its arguments.
250 func HTMLEscaper(args ...interface{}) string {
251         ok := false
252         var s string
253         if len(args) == 1 {
254                 s, ok = args[0].(string)
255         }
256         if !ok {
257                 s = fmt.Sprint(args...)
258         }
259         return HTMLEscapeString(s)
260 }
261
262 // JavaScript escaping.
263
264 var (
265         jsLowUni = []byte(`\u00`)
266         hex      = []byte("0123456789ABCDEF")
267
268         jsBackslash = []byte(`\\`)
269         jsApos      = []byte(`\'`)
270         jsQuot      = []byte(`\"`)
271         jsLt        = []byte(`\x3C`)
272         jsGt        = []byte(`\x3E`)
273 )
274
275 // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b.
276 func JSEscape(w io.Writer, b []byte) {
277         last := 0
278         for i := 0; i < len(b); i++ {
279                 c := b[i]
280
281                 if !jsIsSpecial(rune(c)) {
282                         // fast path: nothing to do
283                         continue
284                 }
285                 w.Write(b[last:i])
286
287                 if c < utf8.RuneSelf {
288                         // Quotes, slashes and angle brackets get quoted.
289                         // Control characters get written as \u00XX.
290                         switch c {
291                         case '\\':
292                                 w.Write(jsBackslash)
293                         case '\'':
294                                 w.Write(jsApos)
295                         case '"':
296                                 w.Write(jsQuot)
297                         case '<':
298                                 w.Write(jsLt)
299                         case '>':
300                                 w.Write(jsGt)
301                         default:
302                                 w.Write(jsLowUni)
303                                 t, b := c>>4, c&0x0f
304                                 w.Write(hex[t : t+1])
305                                 w.Write(hex[b : b+1])
306                         }
307                 } else {
308                         // Unicode rune.
309                         r, size := utf8.DecodeRune(b[i:])
310                         if unicode.IsPrint(r) {
311                                 w.Write(b[i : i+size])
312                         } else {
313                                 // TODO(dsymonds): Do this without fmt?
314                                 fmt.Fprintf(w, "\\u%04X", r)
315                         }
316                         i += size - 1
317                 }
318                 last = i + 1
319         }
320         w.Write(b[last:])
321 }
322
323 // JSEscapeString returns the escaped JavaScript equivalent of the plain text data s.
324 func JSEscapeString(s string) string {
325         // Avoid allocation if we can.
326         if strings.IndexFunc(s, jsIsSpecial) < 0 {
327                 return s
328         }
329         var b bytes.Buffer
330         JSEscape(&b, []byte(s))
331         return b.String()
332 }
333
334 func jsIsSpecial(r rune) bool {
335         switch r {
336         case '\\', '\'', '"', '<', '>':
337                 return true
338         }
339         return r < ' ' || utf8.RuneSelf <= r
340 }
341
342 // JSEscaper returns the escaped JavaScript equivalent of the textual
343 // representation of its arguments.
344 func JSEscaper(args ...interface{}) string {
345         ok := false
346         var s string
347         if len(args) == 1 {
348                 s, ok = args[0].(string)
349         }
350         if !ok {
351                 s = fmt.Sprint(args...)
352         }
353         return JSEscapeString(s)
354 }
355
356 // URLQueryEscaper returns the escaped value of the textual representation of
357 // its arguments in a form suitable for embedding in a URL query.
358 func URLQueryEscaper(args ...interface{}) string {
359         s, ok := "", false
360         if len(args) == 1 {
361                 s, ok = args[0].(string)
362         }
363         if !ok {
364                 s = fmt.Sprint(args...)
365         }
366         return url.QueryEscape(s)
367 }