Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / intercept.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         "log"
9         "reflect"
10         "sort"
11 )
12
13 // An InterceptorFunc is functionality invoked by the framework BEFORE or AFTER
14 // an action.
15 //
16 // An interceptor may optionally return a Result (instead of nil).  Depending on
17 // when the interceptor was invoked, the response is different:
18 // 1. BEFORE:  No further interceptors are invoked, and neither is the action.
19 // 2. AFTER: Further interceptors are still run.
20 // In all cases, any returned Result will take the place of any existing Result.
21 //
22 // In the BEFORE case, that returned Result is guaranteed to be final, while
23 // in the AFTER case it is possible that a further interceptor could emit its
24 // own Result.
25 //
26 // Interceptors are called in the order that they are added.
27 //
28 // ***
29 //
30 // Two types of interceptors are provided: Funcs and Methods
31 //
32 // Func Interceptors may apply to any / all Controllers.
33 //
34 //   func example(*revel.Controller) revel.Result
35 //
36 // Method Interceptors are provided so that properties can be set on application
37 // controllers.
38 //
39 //   func (c AppController) example() revel.Result
40 //   func (c *AppController) example() revel.Result
41 //
42 type InterceptorFunc func(*Controller) Result
43 type InterceptorMethod interface{}
44 type When int
45
46 const (
47         BEFORE When = iota
48         AFTER
49         PANIC
50         FINALLY
51 )
52
53 type InterceptTarget int
54
55 const (
56         AllControllers InterceptTarget = iota
57 )
58
59 type Interception struct {
60         When When
61
62         function InterceptorFunc
63         method   InterceptorMethod
64
65         callable     reflect.Value
66         target       reflect.Type
67         interceptAll bool
68 }
69
70 // Invoke performs the given interception.
71 // val is a pointer to the App Controller.
72 func (i Interception) Invoke(val reflect.Value, target *reflect.Value) reflect.Value {
73         var arg reflect.Value
74         if i.function == nil {
75                 // If it's an InterceptorMethod, then we have to pass in the target type.
76                 arg = *target
77         } else {
78                 // If it's an InterceptorFunc, then the type must be *Controller.
79                 // We can find that by following the embedded types up the chain.
80                 for val.Type() != controllerPtrType {
81                         if val.Kind() == reflect.Ptr {
82                                 val = val.Elem()
83                         }
84                         val = val.Field(0)
85                 }
86                 arg = val
87         }
88
89         vals := i.callable.Call([]reflect.Value{arg})
90         return vals[0]
91 }
92
93 func InterceptorFilter(c *Controller, fc []Filter) {
94         defer invokeInterceptors(FINALLY, c)
95         defer func() {
96                 if err := recover(); err != nil {
97                         invokeInterceptors(PANIC, c)
98                         panic(err)
99                 }
100         }()
101
102         // Invoke the BEFORE interceptors and return early, if we get a result.
103         invokeInterceptors(BEFORE, c)
104         if c.Result != nil {
105                 return
106         }
107
108         fc[0](c, fc[1:])
109         invokeInterceptors(AFTER, c)
110 }
111
112 func invokeInterceptors(when When, c *Controller) {
113         var (
114                 app    = reflect.ValueOf(c.AppController)
115                 result Result
116         )
117
118         for _, intc := range getInterceptors(when, app) {
119                 resultValue := intc.Interceptor.Invoke(app, &intc.Target)
120                 if !resultValue.IsNil() {
121                         result = resultValue.Interface().(Result)
122                 }
123                 if when == BEFORE && result != nil {
124                         c.Result = result
125                         return
126                 }
127         }
128         if result != nil {
129                 c.Result = result
130         }
131 }
132
133 var interceptors []*Interception
134
135 // InterceptFunc installs a general interceptor.
136 // This can be applied to any Controller.
137 // It must have the signature of:
138 //   func example(c *revel.Controller) revel.Result
139 func InterceptFunc(intc InterceptorFunc, when When, target interface{}) {
140         interceptors = append(interceptors, &Interception{
141                 When:         when,
142                 function:     intc,
143                 callable:     reflect.ValueOf(intc),
144                 target:       reflect.TypeOf(target),
145                 interceptAll: target == AllControllers,
146         })
147 }
148
149 // InterceptMethod installs an interceptor method that applies to its own Controller.
150 //   func (c AppController) example() revel.Result
151 //   func (c *AppController) example() revel.Result
152 func InterceptMethod(intc InterceptorMethod, when When) {
153         methodType := reflect.TypeOf(intc)
154         if methodType.Kind() != reflect.Func || methodType.NumOut() != 1 || methodType.NumIn() != 1 {
155                 log.Fatalln("Interceptor method should have signature like",
156                         "'func (c *AppController) example() revel.Result' but was", methodType)
157         }
158
159         interceptors = append(interceptors, &Interception{
160                 When:     when,
161                 method:   intc,
162                 callable: reflect.ValueOf(intc),
163                 target:   methodType.In(0),
164         })
165 }
166
167 // This item is used to provide a sortable set to be returned to the caller. This ensures calls order is maintained
168 //
169 type interceptorItem struct {
170         Interceptor *Interception
171         Target      reflect.Value
172         Level       int
173 }
174 type interceptorItemList []*interceptorItem
175
176 func (a interceptorItemList) Len() int           { return len(a) }
177 func (a interceptorItemList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
178 func (a interceptorItemList) Less(i, j int) bool { return a[i].Level < a[j].Level }
179
180 type reverseInterceptorItemList []*interceptorItem
181
182 func (a reverseInterceptorItemList) Len() int           { return len(a) }
183 func (a reverseInterceptorItemList) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
184 func (a reverseInterceptorItemList) Less(i, j int) bool { return a[i].Level > a[j].Level }
185 func getInterceptors(when When, val reflect.Value) interceptorItemList {
186         result := interceptorItemList{}
187         for _, intc := range interceptors {
188                 if intc.When != when {
189                         continue
190                 }
191
192                 level, target := findTarget(val, intc.target)
193                 if intc.interceptAll || target.IsValid() {
194                         result = append(result, &interceptorItem{intc, target, level})
195                 }
196         }
197
198         // Before is deepest to highest
199         if when == BEFORE {
200                 sort.Sort(result)
201         } else {
202                 // Everything else is highest to deepest
203                 sort.Sort(reverseInterceptorItemList(result))
204         }
205         return result
206 }
207
208 // Find the value of the target, starting from val and including embedded types.
209 // Also, convert between any difference in indirection.
210 // If the target couldn't be found, the returned Value will have IsValid() == false
211 func findTarget(val reflect.Value, target reflect.Type) (int, reflect.Value) {
212         // Look through the embedded types (until we reach the *revel.Controller at the top).
213         valueQueue := []reflect.Value{val}
214         level := 0
215         for len(valueQueue) > 0 {
216                 val, valueQueue = valueQueue[0], valueQueue[1:]
217
218                 // Check if val is of a similar type to the target type.
219                 if val.Type() == target {
220                         return level, val
221                 }
222                 if val.Kind() == reflect.Ptr && val.Elem().Type() == target {
223                         return level, val.Elem()
224                 }
225                 if target.Kind() == reflect.Ptr && target.Elem() == val.Type() {
226                         return level, val.Addr()
227                 }
228
229                 // If we reached the *revel.Controller and still didn't find what we were
230                 // looking for, give up.
231                 if val.Type() == controllerPtrType {
232                         continue
233                 }
234
235                 // Else, add each anonymous field to the queue.
236                 if val.Kind() == reflect.Ptr {
237                         val = val.Elem()
238                 }
239
240                 for i := 0; i < val.NumField(); i++ {
241                         if val.Type().Field(i).Anonymous {
242                                 valueQueue = append(valueQueue, val.Field(i))
243                         }
244                 }
245                 level--
246         }
247
248         return level, reflect.Value{}
249 }