Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / binder.go
1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "encoding/json"
9         "fmt"
10         "io"
11         "io/ioutil"
12         "mime/multipart"
13         "os"
14         "reflect"
15         "strconv"
16         "strings"
17         "time"
18 )
19
20 // A Binder translates between string parameters and Go data structures.
21 type Binder struct {
22         // Bind takes the name and type of the desired parameter and constructs it
23         // from one or more values from Params.
24         //
25         // Example
26         //
27         // Request:
28         //   url?id=123&ol[0]=1&ol[1]=2&ul[]=str&ul[]=array&user.Name=rob
29         //
30         // Action:
31         //   Example.Action(id int, ol []int, ul []string, user User)
32         //
33         // Calls:
34         //   Bind(params, "id", int): 123
35         //   Bind(params, "ol", []int): {1, 2}
36         //   Bind(params, "ul", []string): {"str", "array"}
37         //   Bind(params, "user", User): User{Name:"rob"}
38         //
39         // Note that only exported struct fields may be bound.
40         Bind func(params *Params, name string, typ reflect.Type) reflect.Value
41
42         // Unbind serializes a given value to one or more URL parameters of the given
43         // name.
44         Unbind func(output map[string]string, name string, val interface{})
45 }
46
47 var binderLog = RevelLog.New("section", "binder")
48
49 // ValueBinder is adapter for easily making one-key-value binders.
50 func ValueBinder(f func(value string, typ reflect.Type) reflect.Value) func(*Params, string, reflect.Type) reflect.Value {
51         return func(params *Params, name string, typ reflect.Type) reflect.Value {
52                 vals, ok := params.Values[name]
53                 if !ok || len(vals) == 0 {
54                         return reflect.Zero(typ)
55                 }
56                 return f(vals[0], typ)
57         }
58 }
59
60 // Revel's default date and time constants
61 const (
62         DefaultDateFormat     = "2006-01-02"
63         DefaultDateTimeFormat = "2006-01-02 15:04"
64 )
65
66 // Binders type and kind definition
67 var (
68         // These are the lookups to find a Binder for any type of data.
69         // The most specific binder found will be used (Type before Kind)
70         TypeBinders = make(map[reflect.Type]Binder)
71         KindBinders = make(map[reflect.Kind]Binder)
72
73         // Applications can add custom time formats to this array, and they will be
74         // automatically attempted when binding a time.Time.
75         TimeFormats = []string{}
76
77         DateFormat     string
78         DateTimeFormat string
79         TimeZone       = time.UTC
80
81         IntBinder = Binder{
82                 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
83                         if len(val) == 0 {
84                                 return reflect.Zero(typ)
85                         }
86                         intValue, err := strconv.ParseInt(val, 10, 64)
87                         if err != nil {
88                                 binderLog.Warn("IntBinder Conversion Error", "error", err)
89                                 return reflect.Zero(typ)
90                         }
91                         pValue := reflect.New(typ)
92                         pValue.Elem().SetInt(intValue)
93                         return pValue.Elem()
94                 }),
95                 Unbind: func(output map[string]string, key string, val interface{}) {
96                         output[key] = fmt.Sprintf("%d", val)
97                 },
98         }
99
100         UintBinder = Binder{
101                 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
102                         if len(val) == 0 {
103                                 return reflect.Zero(typ)
104                         }
105                         uintValue, err := strconv.ParseUint(val, 10, 64)
106                         if err != nil {
107                                 binderLog.Warn("UintBinder Conversion Error", "error", err)
108                                 return reflect.Zero(typ)
109                         }
110                         pValue := reflect.New(typ)
111                         pValue.Elem().SetUint(uintValue)
112                         return pValue.Elem()
113                 }),
114                 Unbind: func(output map[string]string, key string, val interface{}) {
115                         output[key] = fmt.Sprintf("%d", val)
116                 },
117         }
118
119         FloatBinder = Binder{
120                 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
121                         if len(val) == 0 {
122                                 return reflect.Zero(typ)
123                         }
124                         floatValue, err := strconv.ParseFloat(val, 64)
125                         if err != nil {
126                                 binderLog.Warn("FloatBinder Conversion Error", "error", err)
127                                 return reflect.Zero(typ)
128                         }
129                         pValue := reflect.New(typ)
130                         pValue.Elem().SetFloat(floatValue)
131                         return pValue.Elem()
132                 }),
133                 Unbind: func(output map[string]string, key string, val interface{}) {
134                         output[key] = fmt.Sprintf("%f", val)
135                 },
136         }
137
138         StringBinder = Binder{
139                 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
140                         return reflect.ValueOf(val)
141                 }),
142                 Unbind: func(output map[string]string, name string, val interface{}) {
143                         output[name] = val.(string)
144                 },
145         }
146
147         // Booleans support a various value formats,
148         // refer `revel.Atob` method.
149         BoolBinder = Binder{
150                 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
151                         return reflect.ValueOf(Atob(val))
152                 }),
153                 Unbind: func(output map[string]string, name string, val interface{}) {
154                         output[name] = fmt.Sprintf("%t", val)
155                 },
156         }
157
158         PointerBinder = Binder{
159                 Bind: func(params *Params, name string, typ reflect.Type) reflect.Value {
160                         v := Bind(params, name, typ.Elem())
161                         if v.CanAddr() {
162                                 return v.Addr()
163                         }
164
165                         return v
166                 },
167                 Unbind: func(output map[string]string, name string, val interface{}) {
168                         Unbind(output, name, reflect.ValueOf(val).Elem().Interface())
169                 },
170         }
171
172         TimeBinder = Binder{
173                 Bind: ValueBinder(func(val string, typ reflect.Type) reflect.Value {
174                         for _, f := range TimeFormats {
175                                 if r, err := time.ParseInLocation(f, val, TimeZone); err == nil {
176                                         return reflect.ValueOf(r)
177                                 }
178                         }
179                         return reflect.Zero(typ)
180                 }),
181                 Unbind: func(output map[string]string, name string, val interface{}) {
182                         var (
183                                 t       = val.(time.Time)
184                                 format  = DateTimeFormat
185                                 h, m, s = t.Clock()
186                         )
187                         if h == 0 && m == 0 && s == 0 {
188                                 format = DateFormat
189                         }
190                         output[name] = t.Format(format)
191                 },
192         }
193
194         MapBinder = Binder{
195                 Bind:   bindMap,
196                 Unbind: unbindMap,
197         }
198 )
199
200 // Used to keep track of the index for individual keyvalues.
201 type sliceValue struct {
202         index int           // Index extracted from brackets.  If -1, no index was provided.
203         value reflect.Value // the bound value for this slice element.
204 }
205
206 // This function creates a slice of the given type, Binds each of the individual
207 // elements, and then sets them to their appropriate location in the slice.
208 // If elements are provided without an explicit index, they are added (in
209 // unspecified order) to the end of the slice.
210 func bindSlice(params *Params, name string, typ reflect.Type) reflect.Value {
211         // Collect an array of slice elements with their indexes (and the max index).
212         maxIndex := -1
213         numNoIndex := 0
214         sliceValues := []sliceValue{}
215
216         // Factor out the common slice logic (between form values and files).
217         processElement := func(key string, vals []string, files []*multipart.FileHeader) {
218                 if !strings.HasPrefix(key, name+"[") {
219                         return
220                 }
221
222                 // Extract the index, and the index where a sub-key starts. (e.g. field[0].subkey)
223                 index := -1
224                 leftBracket, rightBracket := len(name), strings.Index(key[len(name):], "]")+len(name)
225                 if rightBracket > leftBracket+1 {
226                         index, _ = strconv.Atoi(key[leftBracket+1 : rightBracket])
227                 }
228                 subKeyIndex := rightBracket + 1
229
230                 // Handle the indexed case.
231                 if index > -1 {
232                         if index > maxIndex {
233                                 maxIndex = index
234                         }
235                         sliceValues = append(sliceValues, sliceValue{
236                                 index: index,
237                                 value: Bind(params, key[:subKeyIndex], typ.Elem()),
238                         })
239                         return
240                 }
241
242                 // It's an un-indexed element.  (e.g. element[])
243                 numNoIndex += len(vals) + len(files)
244                 for _, val := range vals {
245                         // Unindexed values can only be direct-bound.
246                         sliceValues = append(sliceValues, sliceValue{
247                                 index: -1,
248                                 value: BindValue(val, typ.Elem()),
249                         })
250                 }
251
252                 for _, fileHeader := range files {
253                         sliceValues = append(sliceValues, sliceValue{
254                                 index: -1,
255                                 value: BindFile(fileHeader, typ.Elem()),
256                         })
257                 }
258         }
259
260         for key, vals := range params.Values {
261                 processElement(key, vals, nil)
262         }
263         for key, fileHeaders := range params.Files {
264                 processElement(key, nil, fileHeaders)
265         }
266
267         resultArray := reflect.MakeSlice(typ, maxIndex+1, maxIndex+1+numNoIndex)
268         for _, sv := range sliceValues {
269                 if sv.index != -1 {
270                         resultArray.Index(sv.index).Set(sv.value)
271                 } else {
272                         resultArray = reflect.Append(resultArray, sv.value)
273                 }
274         }
275
276         return resultArray
277 }
278
279 // Break on dots and brackets.
280 // e.g. bar => "bar", bar.baz => "bar", bar[0] => "bar"
281 func nextKey(key string) string {
282         fieldLen := strings.IndexAny(key, ".[")
283         if fieldLen == -1 {
284                 return key
285         }
286         return key[:fieldLen]
287 }
288
289 func unbindSlice(output map[string]string, name string, val interface{}) {
290         v := reflect.ValueOf(val)
291         for i := 0; i < v.Len(); i++ {
292                 Unbind(output, fmt.Sprintf("%s[%d]", name, i), v.Index(i).Interface())
293         }
294 }
295
296 func bindStruct(params *Params, name string, typ reflect.Type) reflect.Value {
297         resultPointer := reflect.New(typ)
298         result := resultPointer.Elem()
299         if params.JSON != nil {
300                 // Try to inject the response as a json into the created result
301                 if err := json.Unmarshal(params.JSON, resultPointer.Interface()); err != nil {
302                         binderLog.Error("bindStruct Unable to unmarshal request", "name", name, "error", err, "data", string(params.JSON))
303                 }
304                 return result
305         }
306         fieldValues := make(map[string]reflect.Value)
307         for key := range params.Values {
308                 if !strings.HasPrefix(key, name+".") {
309                         continue
310                 }
311
312                 // Get the name of the struct property.
313                 // Strip off the prefix. e.g. foo.bar.baz => bar.baz
314                 suffix := key[len(name)+1:]
315                 fieldName := nextKey(suffix)
316                 fieldLen := len(fieldName)
317
318                 if _, ok := fieldValues[fieldName]; !ok {
319                         // Time to bind this field.  Get it and make sure we can set it.
320                         fieldValue := result.FieldByName(fieldName)
321                         if !fieldValue.IsValid() {
322                                 binderLog.Warn("bindStruct Field not found", "name", fieldName)
323                                 continue
324                         }
325                         if !fieldValue.CanSet() {
326                                 binderLog.Warn("bindStruct Field not settable", "name", fieldName)
327                                 continue
328                         }
329                         boundVal := Bind(params, key[:len(name)+1+fieldLen], fieldValue.Type())
330                         fieldValue.Set(boundVal)
331                         fieldValues[fieldName] = boundVal
332                 }
333         }
334
335         return result
336 }
337
338 func unbindStruct(output map[string]string, name string, iface interface{}) {
339         val := reflect.ValueOf(iface)
340         typ := val.Type()
341         for i := 0; i < val.NumField(); i++ {
342                 structField := typ.Field(i)
343                 fieldValue := val.Field(i)
344
345                 // PkgPath is specified to be empty exactly for exported fields.
346                 if structField.PkgPath == "" {
347                         Unbind(output, fmt.Sprintf("%s.%s", name, structField.Name), fieldValue.Interface())
348                 }
349         }
350 }
351
352 // Helper that returns an upload of the given name, or nil.
353 func getMultipartFile(params *Params, name string) multipart.File {
354         for _, fileHeader := range params.Files[name] {
355                 file, err := fileHeader.Open()
356                 if err == nil {
357                         return file
358                 }
359                 binderLog.Warn("getMultipartFile: Failed to open uploaded file", "name", name, "error", err)
360         }
361         return nil
362 }
363
364 func bindFile(params *Params, name string, typ reflect.Type) reflect.Value {
365         reader := getMultipartFile(params, name)
366         if reader == nil {
367                 return reflect.Zero(typ)
368         }
369
370         // If it's already stored in a temp file, just return that.
371         if osFile, ok := reader.(*os.File); ok {
372                 return reflect.ValueOf(osFile)
373         }
374
375         // Otherwise, have to store it.
376         tmpFile, err := ioutil.TempFile("", "revel-upload")
377         if err != nil {
378                 binderLog.Warn("bindFile: Failed to create a temp file to store upload", "name", name, "error", err)
379                 return reflect.Zero(typ)
380         }
381
382         // Register it to be deleted after the request is done.
383         params.tmpFiles = append(params.tmpFiles, tmpFile)
384
385         _, err = io.Copy(tmpFile, reader)
386         if err != nil {
387                 binderLog.Warn("bindFile: Failed to copy upload to temp file", "name", name, "error", err)
388                 return reflect.Zero(typ)
389         }
390
391         _, err = tmpFile.Seek(0, 0)
392         if err != nil {
393                 binderLog.Warn("bindFile: Failed to seek to beginning of temp file", "name", name, "error", err)
394                 return reflect.Zero(typ)
395         }
396
397         return reflect.ValueOf(tmpFile)
398 }
399
400 func bindByteArray(params *Params, name string, typ reflect.Type) reflect.Value {
401         if reader := getMultipartFile(params, name); reader != nil {
402                 b, err := ioutil.ReadAll(reader)
403                 if err == nil {
404                         return reflect.ValueOf(b)
405                 }
406                 binderLog.Warn("bindByteArray: Error reading uploaded file contents", "name", name, "error", err)
407         }
408         return reflect.Zero(typ)
409 }
410
411 func bindReadSeeker(params *Params, name string, typ reflect.Type) reflect.Value {
412         if reader := getMultipartFile(params, name); reader != nil {
413                 return reflect.ValueOf(reader.(io.ReadSeeker))
414         }
415         return reflect.Zero(typ)
416 }
417
418 // bindMap converts parameters using map syntax into the corresponding map. e.g.:
419 //   params["a[5]"]=foo, name="a", typ=map[int]string => map[int]string{5: "foo"}
420 func bindMap(params *Params, name string, typ reflect.Type) reflect.Value {
421         var (
422                 keyType   = typ.Key()
423                 valueType = typ.Elem()
424                 resultPtr = reflect.New(reflect.MapOf(keyType, valueType))
425                 result    = resultPtr.Elem()
426         )
427         result.Set(reflect.MakeMap(typ))
428         if params.JSON != nil {
429                 // Try to inject the response as a json into the created result
430                 if err := json.Unmarshal(params.JSON, resultPtr.Interface()); err != nil {
431                         binderLog.Warn("bindMap: Unable to unmarshal request", "name", name, "error", err)
432                 }
433                 return result
434         }
435
436         for paramName := range params.Values {
437                 // The paramName string must start with the value in the "name" parameter,
438                 // otherwise there is no way the parameter is part of the map
439                 if !strings.HasPrefix(paramName, name) {
440                         continue
441                 }
442
443                 suffix := paramName[len(name)+1:]
444                 fieldName := nextKey(suffix)
445                 if fieldName != "" {
446                         fieldName = fieldName[:len(fieldName)-1]
447                 }
448                 if !strings.HasPrefix(paramName, name+"["+fieldName+"]") {
449                         continue
450                 }
451
452                 result.SetMapIndex(BindValue(fieldName, keyType), Bind(params, name+"["+fieldName+"]", valueType))
453         }
454         return result
455 }
456
457 func unbindMap(output map[string]string, name string, iface interface{}) {
458         mapValue := reflect.ValueOf(iface)
459         for _, key := range mapValue.MapKeys() {
460                 Unbind(output, name+"["+fmt.Sprintf("%v", key.Interface())+"]",
461                         mapValue.MapIndex(key).Interface())
462         }
463 }
464
465 // Bind takes the name and type of the desired parameter and constructs it
466 // from one or more values from Params.
467 // Returns the zero value of the type upon any sort of failure.
468 func Bind(params *Params, name string, typ reflect.Type) reflect.Value {
469         if binder, found := binderForType(typ); found {
470                 return binder.Bind(params, name, typ)
471         }
472         return reflect.Zero(typ)
473 }
474
475 func BindValue(val string, typ reflect.Type) reflect.Value {
476         return Bind(&Params{Values: map[string][]string{"": {val}}}, "", typ)
477 }
478
479 func BindFile(fileHeader *multipart.FileHeader, typ reflect.Type) reflect.Value {
480         return Bind(&Params{Files: map[string][]*multipart.FileHeader{"": {fileHeader}}}, "", typ)
481 }
482
483 func Unbind(output map[string]string, name string, val interface{}) {
484         if binder, found := binderForType(reflect.TypeOf(val)); found {
485                 if binder.Unbind != nil {
486                         binder.Unbind(output, name, val)
487                 } else {
488                         binderLog.Error("Unbind: Unable to unmarshal request", "name", name, "value", val)
489                 }
490         }
491 }
492
493 func binderForType(typ reflect.Type) (Binder, bool) {
494         binder, ok := TypeBinders[typ]
495         if !ok {
496                 binder, ok = KindBinders[typ.Kind()]
497                 if !ok {
498                         binderLog.Error("binderForType: no binder for type", "type", typ)
499                         return Binder{}, false
500                 }
501         }
502         return binder, true
503 }
504
505 // Sadly, the binder lookups can not be declared initialized -- that results in
506 // an "initialization loop" compile error.
507 func init() {
508         KindBinders[reflect.Int] = IntBinder
509         KindBinders[reflect.Int8] = IntBinder
510         KindBinders[reflect.Int16] = IntBinder
511         KindBinders[reflect.Int32] = IntBinder
512         KindBinders[reflect.Int64] = IntBinder
513
514         KindBinders[reflect.Uint] = UintBinder
515         KindBinders[reflect.Uint8] = UintBinder
516         KindBinders[reflect.Uint16] = UintBinder
517         KindBinders[reflect.Uint32] = UintBinder
518         KindBinders[reflect.Uint64] = UintBinder
519
520         KindBinders[reflect.Float32] = FloatBinder
521         KindBinders[reflect.Float64] = FloatBinder
522
523         KindBinders[reflect.String] = StringBinder
524         KindBinders[reflect.Bool] = BoolBinder
525         KindBinders[reflect.Slice] = Binder{bindSlice, unbindSlice}
526         KindBinders[reflect.Struct] = Binder{bindStruct, unbindStruct}
527         KindBinders[reflect.Ptr] = PointerBinder
528         KindBinders[reflect.Map] = MapBinder
529
530         TypeBinders[reflect.TypeOf(time.Time{})] = TimeBinder
531
532         // Uploads
533         TypeBinders[reflect.TypeOf(&os.File{})] = Binder{bindFile, nil}
534         TypeBinders[reflect.TypeOf([]byte{})] = Binder{bindByteArray, nil}
535         TypeBinders[reflect.TypeOf((*io.Reader)(nil)).Elem()] = Binder{bindReadSeeker, nil}
536         TypeBinders[reflect.TypeOf((*io.ReadSeeker)(nil)).Elem()] = Binder{bindReadSeeker, nil}
537
538         OnAppStart(func() {
539                 DateTimeFormat = Config.StringDefault("format.datetime", DefaultDateTimeFormat)
540                 DateFormat = Config.StringDefault("format.date", DefaultDateFormat)
541                 TimeFormats = append(TimeFormats, DateTimeFormat, DateFormat)
542         })
543 }