--- /dev/null
+package revel
+
+import (
+ "reflect"
+ "strings"
+)
+
+// Controller registry and types.
+type ControllerType struct {
+ Namespace string // The namespace of the controller
+ ModuleSource *Module // The module for the controller
+ Type reflect.Type
+ Methods []*MethodType
+ ControllerIndexes [][]int // FieldByIndex to all embedded *Controllers
+ ControllerEvents *ControllerTypeEvents
+}
+type ControllerTypeEvents struct {
+ Before, After, Finally, Panic []*ControllerFieldPath
+}
+
+// The controller field path provides the caller the ability to invoke the call
+// directly
+type ControllerFieldPath struct {
+ IsPointer bool
+ FieldIndexPath []int
+ FunctionCall reflect.Value
+}
+
+type MethodType struct {
+ Name string
+ Args []*MethodArg
+ RenderArgNames map[int][]string
+ lowerName string
+ Index int
+}
+
+type MethodArg struct {
+ Name string
+ Type reflect.Type
+}
+
+// Adds the controller to the controllers map using its namespace, also adds it to the module list of controllers.
+// If the controller is in the main application it is added without its namespace as well.
+func AddControllerType(moduleSource *Module, controllerType reflect.Type, methods []*MethodType) (newControllerType *ControllerType) {
+ if moduleSource == nil {
+ moduleSource = appModule
+ }
+
+ newControllerType = &ControllerType{ModuleSource: moduleSource, Type: controllerType, Methods: methods, ControllerIndexes: findControllers(controllerType)}
+ newControllerType.ControllerEvents = NewControllerTypeEvents(newControllerType)
+ newControllerType.Namespace = moduleSource.Namespace()
+ controllerName := newControllerType.Name()
+
+ // Store the first controller only in the controllers map with the unmapped namespace.
+ if _, found := controllers[controllerName]; !found {
+ controllers[controllerName] = newControllerType
+ newControllerType.ModuleSource.AddController(newControllerType)
+ if newControllerType.ModuleSource == appModule {
+ // Add the controller mapping into the global namespace
+ controllers[newControllerType.ShortName()] = newControllerType
+ }
+ } else {
+ controllerLog.Errorf("Error, attempt to register duplicate controller as %s", controllerName)
+ }
+ controllerLog.Debugf("Registered controller: %s", controllerName)
+
+ return
+}
+
+// Method searches for a given exported method (case insensitive)
+func (ct *ControllerType) Method(name string) *MethodType {
+ lowerName := strings.ToLower(name)
+ for _, method := range ct.Methods {
+ if method.lowerName == lowerName {
+ return method
+ }
+ }
+ return nil
+}
+
+// The controller name with the namespace
+func (ct *ControllerType) Name() string {
+ return ct.Namespace + ct.ShortName()
+}
+
+// The controller name without the namespace
+func (ct *ControllerType) ShortName() string {
+ return strings.ToLower(ct.Type.Name())
+}
+
+func NewControllerTypeEvents(c *ControllerType) (ce *ControllerTypeEvents) {
+ ce = &ControllerTypeEvents{}
+ // Parse the methods for the controller type, assign any control methods
+ checkType := c.Type
+ ce.check(checkType, []int{})
+ return
+}
+
+// Add in before after panic and finally, recursive call
+// Befores are ordered in revers, everything else is in order of first encountered
+func (cte *ControllerTypeEvents) check(theType reflect.Type, fieldPath []int) {
+ typeChecker := func(checkType reflect.Type) {
+ for index := 0; index < checkType.NumMethod(); index++ {
+ m := checkType.Method(index)
+ // Must be two arguments, the second returns the controller type
+ // Go cannot differentiate between promoted methods and
+ // embedded methods, this allows the embedded method to be run
+ // https://github.com/golang/go/issues/21162
+ if m.Type.NumOut() == 2 && m.Type.Out(1) == checkType {
+ if checkType.Kind() == reflect.Ptr {
+ controllerLog.Debug("Found controller type event method pointer", "name", checkType.Elem().Name(), "methodname", m.Name)
+ } else {
+ controllerLog.Debug("Found controller type event method", "name", checkType.Name(), "methodname", m.Name)
+ }
+ controllerFieldPath := newFieldPath(checkType.Kind() == reflect.Ptr, m.Func, fieldPath)
+ switch strings.ToLower(m.Name) {
+ case "before":
+ cte.Before = append([]*ControllerFieldPath{controllerFieldPath}, cte.Before...)
+ case "after":
+ cte.After = append(cte.After, controllerFieldPath)
+ case "panic":
+ cte.Panic = append(cte.Panic, controllerFieldPath)
+ case "finally":
+ cte.Finally = append(cte.Finally, controllerFieldPath)
+ }
+ }
+ }
+ }
+
+ // Check methods of both types
+ typeChecker(theType)
+ typeChecker(reflect.PtrTo(theType))
+
+ // Check for any sub controllers, ignore any pointers to controllers revel.Controller
+ for i := 0; i < theType.NumField(); i++ {
+ v := theType.Field(i)
+
+ switch v.Type.Kind() {
+ case reflect.Struct:
+ cte.check(v.Type, append(fieldPath, i))
+ }
+ }
+}
+func newFieldPath(isPointer bool, value reflect.Value, fieldPath []int) *ControllerFieldPath {
+ return &ControllerFieldPath{IsPointer: isPointer, FunctionCall: value, FieldIndexPath: fieldPath}
+}
+
+func (fieldPath *ControllerFieldPath) Invoke(value reflect.Value, input []reflect.Value) (result []reflect.Value) {
+ for _, index := range fieldPath.FieldIndexPath {
+ // You can only fetch fields from non pointers
+ if value.Type().Kind() == reflect.Ptr {
+ value = value.Elem().Field(index)
+ } else {
+ value = value.Field(index)
+ }
+ }
+ if fieldPath.IsPointer && value.Type().Kind() != reflect.Ptr {
+ value = value.Addr()
+ } else if !fieldPath.IsPointer && value.Type().Kind() == reflect.Ptr {
+ value = value.Elem()
+ }
+
+ return fieldPath.FunctionCall.Call(append([]reflect.Value{value}, input...))
+}