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...)) }