Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / filterconfig.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         "reflect"
9         "strings"
10 )
11
12 // Map from "Controller" or "Controller.Method" to the Filter chain
13 var filterOverrides = make(map[string][]Filter)
14
15 // FilterConfigurator allows the developer configure the filter chain on a
16 // per-controller or per-action basis.  The filter configuration is applied by
17 // the FilterConfiguringFilter, which is itself a filter stage.  For example,
18 //
19 // Assuming:
20 //   Filters = []Filter{
21 //     RouterFilter,
22 //     FilterConfiguringFilter,
23 //     SessionFilter,
24 //     ActionInvoker,
25 //   }
26 //
27 // Add:
28 //   FilterAction(App.Action).
29 //     Add(OtherFilter)
30 //
31 //   => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
32 //
33 // Remove:
34 //   FilterAction(App.Action).
35 //     Remove(SessionFilter)
36 //
37 //   => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
38 //
39 // Insert:
40 //   FilterAction(App.Action).
41 //     Insert(OtherFilter, revel.BEFORE, SessionFilter)
42 //
43 //   => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
44 //
45 // Filter modifications may be combined between Controller and Action.  For example:
46 //   FilterController(App{}).
47 //     Add(Filter1)
48 //   FilterAction(App.Action).
49 //     Add(Filter2)
50 //
51 //  .. would result in App.Action being filtered by both Filter1 and Filter2.
52 //
53 // Note: the last filter stage is not subject to the configurator.  In
54 // particular, Add() adds a filter to the second-to-last place.
55 type FilterConfigurator struct {
56         key            string // e.g. "App", "App.Action"
57         controllerName string // e.g. "App"
58 }
59
60 func newFilterConfigurator(controllerName, methodName string) FilterConfigurator {
61         if methodName == "" {
62                 return FilterConfigurator{controllerName, controllerName}
63         }
64         return FilterConfigurator{controllerName + "." + methodName, controllerName}
65 }
66
67 // FilterController returns a configurator for the filters applied to all
68 // actions on the given controller instance.  For example:
69 //   FilterController(MyController{})
70 func FilterController(controllerInstance interface{}) FilterConfigurator {
71         t := reflect.TypeOf(controllerInstance)
72         for t.Kind() == reflect.Ptr {
73                 t = t.Elem()
74         }
75         return newFilterConfigurator(t.Name(), "")
76 }
77
78 // FilterAction returns a configurator for the filters applied to the given
79 // controller method. For example:
80 //   FilterAction(MyController.MyAction)
81 func FilterAction(methodRef interface{}) FilterConfigurator {
82         var (
83                 methodValue = reflect.ValueOf(methodRef)
84                 methodType  = methodValue.Type()
85         )
86         if methodType.Kind() != reflect.Func || methodType.NumIn() == 0 {
87                 panic("Expecting a controller method reference (e.g. Controller.Action), got a " +
88                         methodType.String())
89         }
90
91         controllerType := methodType.In(0)
92         method := FindMethod(controllerType, methodValue)
93         if method == nil {
94                 panic("Action not found on controller " + controllerType.Name())
95         }
96
97         for controllerType.Kind() == reflect.Ptr {
98                 controllerType = controllerType.Elem()
99         }
100
101         return newFilterConfigurator(controllerType.Name(), method.Name)
102 }
103
104 // Add the given filter in the second-to-last position in the filter chain.
105 // (Second-to-last so that it is before ActionInvoker)
106 func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
107         conf.apply(func(fc []Filter) []Filter {
108                 return conf.addFilter(f, fc)
109         })
110         return conf
111 }
112
113 func (conf FilterConfigurator) addFilter(f Filter, fc []Filter) []Filter {
114         return append(fc[:len(fc)-1], f, fc[len(fc)-1])
115 }
116
117 // Remove a filter from the filter chain.
118 func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
119         conf.apply(func(fc []Filter) []Filter {
120                 return conf.rmFilter(target, fc)
121         })
122         return conf
123 }
124
125 func (conf FilterConfigurator) rmFilter(target Filter, fc []Filter) []Filter {
126         for i, f := range fc {
127                 if FilterEq(f, target) {
128                         return append(fc[:i], fc[i+1:]...)
129                 }
130         }
131         return fc
132 }
133
134 // Insert a filter into the filter chain before or after another.
135 // This may be called with the BEFORE or AFTER constants, for example:
136 //   revel.FilterAction(App.Index).
137 //     Insert(MyFilter, revel.BEFORE, revel.ActionInvoker).
138 //     Insert(MyFilter2, revel.AFTER, revel.PanicFilter)
139 func (conf FilterConfigurator) Insert(insert Filter, where When, target Filter) FilterConfigurator {
140         if where != BEFORE && where != AFTER {
141                 panic("where must be BEFORE or AFTER")
142         }
143         conf.apply(func(fc []Filter) []Filter {
144                 return conf.insertFilter(insert, where, target, fc)
145         })
146         return conf
147 }
148
149 func (conf FilterConfigurator) insertFilter(insert Filter, where When, target Filter, fc []Filter) []Filter {
150         for i, f := range fc {
151                 if FilterEq(f, target) {
152                         if where == BEFORE {
153                                 return append(fc[:i], append([]Filter{insert}, fc[i:]...)...)
154                         }
155                         return append(fc[:i+1], append([]Filter{insert}, fc[i+1:]...)...)
156                 }
157         }
158         return fc
159 }
160
161 // getChain returns the filter chain that applies to the given controller or
162 // action.  If no overrides are configured, then a copy of the default filter
163 // chain is returned.
164 func (conf FilterConfigurator) getChain() []Filter {
165         var filters []Filter
166         if filters = getOverrideChain(conf.controllerName, conf.key); filters == nil {
167                 // The override starts with all filters after FilterConfiguringFilter
168                 for i, f := range Filters {
169                         if FilterEq(f, FilterConfiguringFilter) {
170                                 filters = make([]Filter, len(Filters)-i-1)
171                                 copy(filters, Filters[i+1:])
172                                 break
173                         }
174                 }
175                 if filters == nil {
176                         panic("FilterConfiguringFilter not found in revel.Filters.")
177                 }
178         }
179         return filters
180 }
181
182 // apply applies the given functional change to the filter overrides.
183 // No other function modifies the filterOverrides map.
184 func (conf FilterConfigurator) apply(f func([]Filter) []Filter) {
185         // Updates any actions that have had their filters overridden, if this is a
186         // Controller configurator.
187         if conf.controllerName == conf.key {
188                 for k, v := range filterOverrides {
189                         if strings.HasPrefix(k, conf.controllerName+".") {
190                                 filterOverrides[k] = f(v)
191                         }
192                 }
193         }
194
195         // Update the Controller or Action overrides.
196         filterOverrides[conf.key] = f(conf.getChain())
197 }
198
199 // FilterEq returns true if the two filters reference the same filter.
200 func FilterEq(a, b Filter) bool {
201         return reflect.ValueOf(a).Pointer() == reflect.ValueOf(b).Pointer()
202 }
203
204 // FilterConfiguringFilter is a filter stage that customizes the remaining
205 // filter chain for the action being invoked.
206 func FilterConfiguringFilter(c *Controller, fc []Filter) {
207         if newChain := getOverrideChain(c.Name, c.Action); newChain != nil {
208                 newChain[0](c, newChain[1:])
209                 return
210         }
211         fc[0](c, fc[1:])
212 }
213
214 // getOverrideChain retrieves the overrides for the action that is set
215 func getOverrideChain(controllerName, action string) []Filter {
216         if newChain, ok := filterOverrides[action]; ok {
217                 return newChain
218         }
219         if newChain, ok := filterOverrides[controllerName]; ok {
220                 return newChain
221         }
222         return nil
223 }