Filterconfig

package revel

// FilterConfigurator allows the developer configure the filter chain on a
// per-controller or per-action basis.  The filter configuration is applied by
// the FilterConfiguringFilter, which is itself a filter stage.  For example,
//
// Assuming:
//   Filters = []Filter{
//     RouterFilter,
//     FilterConfiguringFilter,
//     SessionFilter,
//     ActionInvoker,
//   }
//
// Add:
//   FilterAction(App.Action).
//     Add(OtherFilter)
//
//   => RouterFilter, FilterConfiguringFilter, SessionFilter, OtherFilter, ActionInvoker
//
// Remove:
//   FilterAction(App.Action).
//     Remove(SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, ActionInvoker
//
// Insert:
//   FilterAction(App.Action).
//     Insert(OtherFilter, revel.BEFORE, SessionFilter)
//
//   => RouterFilter, FilterConfiguringFilter, OtherFilter, SessionFilter, ActionInvoker
//
// Filter modifications may be combined between Controller and Action.  For example:
//   FilterController(App{}).
//     Add(Filter1)
//   FilterAction(App.Action).
//     Add(Filter2)
//
//  .. would result in App.Action being filtered by both Filter1 and Filter2.
//
// Note: the last filter stage is not subject to the configurator.  In
// particular, Add() adds a filter to the second-to-last place.
type FilterConfigurator struct {

}

// FilterController returns a configurator for the filters applied to all
// actions on the given controller instance.  For example:
//   FilterAction(MyController{})
func FilterController(controllerInstance interface{}) FilterConfigurator {
    t := reflect.TypeOf(controllerInstance)
    for t.Kind() == reflect.Ptr {
        t = t.Elem()
    }
    return newFilterConfigurator(t.Name(), "")
}

// FilterAction returns a configurator for the filters applied to the given
// controller method. For example:
//   FilterAction(MyController.MyAction)
func FilterAction(methodRef interface{}) FilterConfigurator {
    var (
        methodValue = reflect.ValueOf(methodRef)
        methodType  = methodValue.Type()
    )
    if methodType.Kind() != reflect.Func || methodType.NumIn() == 0 {
        panic("Expecting a controller method reference (e.g. Controller.Action), got a " +
            methodType.String())
    }

    controllerType := methodType.In(0)
    method := FindMethod(controllerType, methodValue)
    if method == nil {
        panic("Action not found on controller " + controllerType.Name())
    }

    for controllerType.Kind() == reflect.Ptr {
        controllerType = controllerType.Elem()
    }

    return newFilterConfigurator(controllerType.Name(), method.Name)
}

// Add the given filter in the second-to-last position in the filter chain.
// (Second-to-last so that it is before ActionInvoker)
func (conf FilterConfigurator) Add(f Filter) FilterConfigurator {
    conf.apply(func(fc []Filter) []Filter {
        return conf.addFilter(f, fc)
    })
    return conf
}

// Remove a filter from the filter chain.
func (conf FilterConfigurator) Remove(target Filter) FilterConfigurator {
    conf.apply(func(fc []Filter) []Filter {
        return conf.rmFilter(target, fc)
    })
    return conf
}

// Insert a filter into the filter chain before or after another.
// This may be called with the BEFORE or AFTER constants, for example:
//   revel.FilterAction(App.Index).
//     Insert(MyFilter, revel.BEFORE, revel.ActionInvoker).
//     Insert(MyFilter2, revel.AFTER, revel.PanicFilter)
func (conf FilterConfigurator) Insert(insert Filter, where When, target Filter) FilterConfigurator {
    if where != BEFORE && where != AFTER {
        panic("where must be BEFORE or AFTER")
    }
    conf.apply(func(fc []Filter) []Filter {
        return conf.insertFilter(insert, where, target, fc)
    })
    return conf
}

// FilterEq returns true if the two filters reference the same filter.
func FilterEq(a, b Filter) bool {
    return reflect.ValueOf(a).Pointer() == reflect.ValueOf(b).Pointer()
}

// FilterConfiguringFilter is a filter stage that customizes the remaining
// filter chain for the action being invoked.
func FilterConfiguringFilter(c *Controller, fc []Filter) {
    if newChain := getOverrideChain(c.Name, c.Action); newChain != nil {
        newChain[0](c, newChain[1:])
        return
    }
    fc[0](c, fc[1:])
}