X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Fcontroller_type.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Fcontroller_type.go;h=4ba2f33f971596769dcbd2dfb600189ff7bbdb0d;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/controller_type.go b/src/foundation/api/revel/controller_type.go new file mode 100644 index 0000000..4ba2f33 --- /dev/null +++ b/src/foundation/api/revel/controller_type.go @@ -0,0 +1,164 @@ +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...)) +}