Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / controller.go
diff --git a/src/foundation/api/revel/controller.go b/src/foundation/api/revel/controller.go
new file mode 100644 (file)
index 0000000..29dbf7c
--- /dev/null
@@ -0,0 +1,546 @@
+// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
+// Revel Framework source code and usage is governed by a MIT style
+// license that can be found in the LICENSE file.
+
+package revel
+
+import (
+       "errors"
+       "fmt"
+       "io"
+       "net/http"
+       "os"
+       "path/filepath"
+       "reflect"
+       "runtime"
+       "strings"
+       "time"
+
+       "github.com/revel/revel/logger"
+       "github.com/revel/revel/session"
+       "github.com/revel/revel/utils"
+)
+
+// Controller Revel's controller structure that gets embedded in user defined
+// controllers
+type Controller struct {
+       Name          string          // The controller name, e.g. "Application"
+       Type          *ControllerType // A description of the controller type.
+       MethodName    string          // The method name, e.g. "Index"
+       MethodType    *MethodType     // A description of the invoked action type.
+       AppController interface{}     // The controller that was instantiated. embeds revel.Controller
+       Action        string          // The fully qualified action name, e.g. "App.Index"
+       ClientIP      string          // holds IP address of request came from
+
+       Request  *Request
+       Response *Response
+       Result   Result
+
+       Flash      Flash                  // User cookie, cleared after 1 request.
+       Session    session.Session        // Session, stored using the session engine specified
+       Params     *Params                // Parameters from URL and form (including multipart).
+       Args       map[string]interface{} // Per-request scratch space.
+       ViewArgs   map[string]interface{} // Variables passed to the template.
+       Validation *Validation            // Data validation helpers
+       Log        logger.MultiLogger     // Context Logger
+}
+
+// The map of controllers, controllers are mapped by using the namespace|controller_name as the key
+var controllers = make(map[string]*ControllerType)
+var controllerLog = RevelLog.New("section", "controller")
+
+// NewController returns new controller instance for Request and Response
+func NewControllerEmpty() *Controller {
+       return &Controller{Request: NewRequest(nil), Response: NewResponse(nil)}
+}
+
+// New controller, creates a new instance wrapping the request and response in it
+func NewController(context ServerContext) *Controller {
+       c := NewControllerEmpty()
+       c.SetController(context)
+       return c
+}
+
+// Sets the request and the response for the controller
+func (c *Controller) SetController(context ServerContext) {
+
+       c.Request.SetRequest(context.GetRequest())
+       c.Response.SetResponse(context.GetResponse())
+       c.Request.controller = c
+       c.Params = new(Params)
+       c.Args = map[string]interface{}{}
+       c.ViewArgs = map[string]interface{}{
+               "RunMode": RunMode,
+               "DevMode": DevMode,
+       }
+
+}
+func (c *Controller) Destroy() {
+       // When the instantiated controller gets injected
+       // It inherits this method, so we need to
+       // check to see if the controller is nil before performing
+       // any actions
+       if c == nil {
+               return
+       }
+       if c.AppController != nil {
+               c.resetAppControllerFields()
+               // Return this instance to the pool
+               appController := c.AppController
+               c.AppController = nil
+               if RevelConfig.Controller.Reuse {
+                       RevelConfig.Controller.CachedMap[c.Name].Push(appController)
+               }
+               c.AppController = nil
+       }
+
+       c.Request.Destroy()
+       c.Response.Destroy()
+       c.Params = nil
+       c.Args = nil
+       c.ViewArgs = nil
+       c.Name = ""
+       c.Type = nil
+       c.MethodName = ""
+       c.MethodType = nil
+       c.Action = ""
+       c.ClientIP = ""
+       c.Result = nil
+       c.Flash = Flash{}
+       c.Session = session.NewSession()
+       c.Params = nil
+       c.Validation = nil
+       c.Log = nil
+}
+
+// FlashParams serializes the contents of Controller.Params to the Flash
+// cookie.
+func (c *Controller) FlashParams() {
+       for key, vals := range c.Params.Values {
+               c.Flash.Out[key] = strings.Join(vals, ",")
+       }
+}
+
+func (c *Controller) SetCookie(cookie *http.Cookie) {
+       c.Response.Out.internalHeader.SetCookie(cookie.String())
+}
+
+type ErrorCoder interface {
+       HTTPCode() int
+}
+
+func (c *Controller) RenderError(err error) Result {
+       if coder, ok := err.(ErrorCoder); ok {
+               c.setStatusIfNil(coder.HTTPCode())
+       } else {
+               c.setStatusIfNil(http.StatusInternalServerError)
+       }
+
+       return ErrorResult{c.ViewArgs, err}
+}
+
+func (c *Controller) setStatusIfNil(status int) {
+       if c.Response.Status == 0 {
+               c.Response.Status = status
+       }
+}
+
+// Render a template corresponding to the calling Controller method.
+// Arguments will be added to c.ViewArgs prior to rendering the template.
+// They are keyed on their local identifier.
+//
+// For example:
+//
+//     func (c Users) ShowUser(id int) revel.Result {
+//              user := loadUser(id)
+//              return c.Render(user)
+//     }
+//
+// This action will render views/Users/ShowUser.html, passing in an extra
+// key-value "user": (User).
+//
+// This is the slower magical version which uses the runtime
+// to determine
+// 1) Set c.ViewArgs to the arguments passed into this function
+// 2) How to call the RenderTemplate by building the following line
+// c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
+//
+// If you want your code to run faster it is recommended you add the template values directly
+// to the c.ViewArgs and call c.RenderTemplate directly
+func (c *Controller) Render(extraViewArgs ...interface{}) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       // Get the calling function line number.
+       _, _, line, ok := runtime.Caller(1)
+       if !ok {
+               controllerLog.Error("Render: Failed to get Caller information")
+       }
+
+       // Get the extra ViewArgs passed in.
+       if renderArgNames, ok := c.MethodType.RenderArgNames[line]; ok {
+               if len(renderArgNames) == len(extraViewArgs) {
+                       for i, extraRenderArg := range extraViewArgs {
+                               c.ViewArgs[renderArgNames[i]] = extraRenderArg
+                       }
+               } else {
+                       controllerLog.Error(fmt.Sprint(len(renderArgNames), "RenderArg names found for",
+                               len(extraViewArgs), "extra ViewArgs"))
+               }
+       } else {
+               controllerLog.Error(fmt.Sprint("No RenderArg names found for Render call on line", line,
+                       "(Action", c.Action, ")"),"stack",logger.NewCallStack())
+       }
+
+       return c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
+}
+
+// RenderTemplate method does less magical way to render a template.
+// Renders the given template, using the current ViewArgs.
+func (c *Controller) RenderTemplate(templatePath string) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       // Get the Template.
+       lang, _ := c.ViewArgs[CurrentLocaleViewArg].(string)
+       template, err := MainTemplateLoader.TemplateLang(templatePath, lang)
+       if err != nil {
+               return c.RenderError(err)
+       }
+
+       return &RenderTemplateResult{
+               Template: template,
+               ViewArgs: c.ViewArgs,
+       }
+}
+
+// TemplateOutput returns the result of the template rendered using the controllers ViewArgs.
+func (c *Controller) TemplateOutput(templatePath string) (data []byte, err error) {
+       return TemplateOutputArgs(templatePath, c.ViewArgs)
+}
+
+// RenderJSON uses encoding/json.Marshal to return JSON to the client.
+func (c *Controller) RenderJSON(o interface{}) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       return RenderJSONResult{o, ""}
+}
+
+// RenderJSONP renders JSONP result using encoding/json.Marshal
+func (c *Controller) RenderJSONP(callback string, o interface{}) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       return RenderJSONResult{o, callback}
+}
+
+// RenderXML uses encoding/xml.Marshal to return XML to the client.
+func (c *Controller) RenderXML(o interface{}) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       return RenderXMLResult{o}
+}
+
+// RenderText renders plaintext in response, printf style.
+func (c *Controller) RenderText(text string, objs ...interface{}) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       finalText := text
+       if len(objs) > 0 {
+               finalText = fmt.Sprintf(text, objs...)
+       }
+       return &RenderTextResult{finalText}
+}
+
+// RenderHTML renders html in response
+func (c *Controller) RenderHTML(html string) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       return &RenderHTMLResult{html}
+}
+
+// Todo returns an HTTP 501 Not Implemented "todo" indicating that the
+// action isn't done yet.
+func (c *Controller) Todo() Result {
+       c.Response.Status = http.StatusNotImplemented
+       controllerLog.Debug("Todo: Not implemented function", "action", c.Action)
+       return c.RenderError(&Error{
+               Title:       "TODO",
+               Description: "This action is not implemented",
+       })
+}
+
+// NotFound returns an HTTP 404 Not Found response whose body is the
+// formatted string of msg and objs.
+func (c *Controller) NotFound(msg string, objs ...interface{}) Result {
+       finalText := msg
+       if len(objs) > 0 {
+               finalText = fmt.Sprintf(msg, objs...)
+       }
+       c.Response.Status = http.StatusNotFound
+       return c.RenderError(&Error{
+               Title:       "Not Found",
+               Description: finalText,
+       })
+}
+
+// Forbidden returns an HTTP 403 Forbidden response whose body is the
+// formatted string of msg and objs.
+func (c *Controller) Forbidden(msg string, objs ...interface{}) Result {
+       finalText := msg
+       if len(objs) > 0 {
+               finalText = fmt.Sprintf(msg, objs...)
+       }
+       c.Response.Status = http.StatusForbidden
+       return c.RenderError(&Error{
+               Title:       "Forbidden",
+               Description: finalText,
+       })
+}
+
+// RenderFileName returns a file indicated by the path as provided via the filename.
+// It can be either displayed inline or downloaded as an attachment.
+// The name and size are taken from the file info.
+func (c *Controller) RenderFileName(filename string, delivery ContentDisposition) Result {
+       f, err := os.Open(filename)
+       if err != nil {
+               c.Log.Errorf("Cant open file: %v", err)
+               return c.RenderError(err)
+       }
+       return c.RenderFile(f, delivery)
+}
+
+// RenderFile returns a file, either displayed inline or downloaded
+// as an attachment. The name and size are taken from the file info.
+func (c *Controller) RenderFile(file *os.File, delivery ContentDisposition) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       var (
+               modtime       = time.Now()
+               fileInfo, err = file.Stat()
+       )
+       if err != nil {
+               controllerLog.Error("RenderFile: error", "error", err)
+       }
+       if fileInfo != nil {
+               modtime = fileInfo.ModTime()
+       }
+       return c.RenderBinary(file, filepath.Base(file.Name()), delivery, modtime)
+}
+
+// RenderBinary is like RenderFile() except that it instead of a file on disk,
+// it renders data from memory (which could be a file that has not been written,
+// the output from some function, or bytes streamed from somewhere else, as long
+// it implements io.Reader).  When called directly on something generated or
+// streamed, modtime should mostly likely be time.Now().
+func (c *Controller) RenderBinary(memfile io.Reader, filename string, delivery ContentDisposition, modtime time.Time) Result {
+       c.setStatusIfNil(http.StatusOK)
+
+       return &BinaryResult{
+               Reader:   memfile,
+               Name:     filename,
+               Delivery: delivery,
+               Length:   -1, // http.ServeContent gets the length itself unless memfile is a stream.
+               ModTime:  modtime,
+       }
+}
+
+// Redirect to an action or to a URL.
+//   c.Redirect(Controller.Action)
+//   c.Redirect("/controller/action")
+//   c.Redirect("/controller/%d/action", id)
+func (c *Controller) Redirect(val interface{}, args ...interface{}) Result {
+       c.setStatusIfNil(http.StatusFound)
+
+       if url, ok := val.(string); ok {
+               if len(args) == 0 {
+                       return &RedirectToURLResult{url}
+               }
+               return &RedirectToURLResult{fmt.Sprintf(url, args...)}
+       }
+       return &RedirectToActionResult{val, args}
+}
+
+// This stats returns some interesting stats based on what is cached in memory
+// and what is available directly
+func (c *Controller) Stats() map[string]interface{} {
+       result := CurrentEngine.Stats()
+       if RevelConfig.Controller.Reuse {
+               result["revel-controllers"] = RevelConfig.Controller.Stack.String()
+               for key, appStack := range RevelConfig.Controller.CachedMap {
+                       result["app-" + key] = appStack.String()
+               }
+       }
+       return result
+}
+
+// Message performs a lookup for the given message name using the given
+// arguments using the current language defined for this controller.
+//
+// The current language is set by the i18n plugin.
+func (c *Controller) Message(message string, args ...interface{}) string {
+       return MessageFunc(c.Request.Locale, message, args...)
+}
+
+// SetAction sets the action that is being invoked in the current request.
+// It sets the following properties: Name, Action, Type, MethodType
+func (c *Controller) SetAction(controllerName, methodName string) error {
+
+       return c.SetTypeAction(controllerName, methodName, nil)
+}
+
+// SetAction sets the assigns the Controller type, sets the action and initializes the controller
+func (c *Controller) SetTypeAction(controllerName, methodName string, typeOfController *ControllerType) error {
+
+       // Look up the controller and method types.
+       if typeOfController == nil {
+               if c.Type = ControllerTypeByName(controllerName, anyModule); c.Type == nil {
+                       return errors.New("revel/controller: failed to find controller " + controllerName)
+               }
+       } else {
+               c.Type = typeOfController
+       }
+
+       // Note method name is case insensitive search
+       if c.MethodType = c.Type.Method(methodName); c.MethodType == nil {
+               return errors.New("revel/controller: failed to find action " + controllerName + "." + methodName)
+       }
+
+       c.Name, c.MethodName = c.Type.Type.Name(), c.MethodType.Name
+       c.Action = c.Name + "." + c.MethodName
+
+       // Update Logger with controller and namespace
+       if c.Log != nil {
+               c.Log = c.Log.New("action", c.Action, "namespace", c.Type.Namespace)
+       }
+
+       if RevelConfig.Controller.Reuse {
+               if _, ok := RevelConfig.Controller.CachedMap[c.Name]; !ok {
+                       // Create a new stack for this controller
+                       localType := c.Type.Type
+                       RevelConfig.Controller.CachedMap[c.Name] = utils.NewStackLock(
+                               RevelConfig.Controller.CachedStackSize,
+                               RevelConfig.Controller.CachedStackMaxSize,
+                               func() interface{} {
+                                       return reflect.New(localType).Interface()
+                               })
+               }
+               // Instantiate the controller.
+               c.AppController = RevelConfig.Controller.CachedMap[c.Name].Pop()
+       } else {
+               c.AppController = reflect.New(c.Type.Type).Interface()
+       }
+       c.setAppControllerFields()
+
+       return nil
+}
+
+func ControllerTypeByName(controllerName string, moduleSource *Module) (c *ControllerType) {
+       var found bool
+       if c, found = controllers[controllerName]; !found {
+               // Backup, passed in controllerName should be in lower case, but may not be
+               if c, found = controllers[strings.ToLower(controllerName)]; !found {
+                       controllerLog.Debug("ControllerTypeByName: Cannot find controller in controllers map ", "controller", controllerName)
+                       // Search for the controller by name
+                       for _, cType := range controllers {
+                               testControllerName := strings.ToLower(cType.Type.Name())
+                               if testControllerName == strings.ToLower(controllerName) && (cType.ModuleSource == moduleSource || moduleSource == anyModule) {
+                                       controllerLog.Warn("ControllerTypeByName: Matched empty namespace controller ", "controller", controllerName, "namespace", cType.ModuleSource.Name)
+                                       c = cType
+                                       found = true
+                                       break
+                               }
+                       }
+               }
+       }
+       return
+}
+
+// Injects this instance (c) into the AppController instance
+func (c *Controller) setAppControllerFields() {
+       appController := reflect.ValueOf(c.AppController).Elem()
+       cValue := reflect.ValueOf(c)
+       for _, index := range c.Type.ControllerIndexes {
+               appController.FieldByIndex(index).Set(cValue)
+       }
+}
+
+// Removes this instance (c) from the AppController instance
+func (c *Controller) resetAppControllerFields() {
+       appController := reflect.ValueOf(c.AppController).Elem()
+       // Zero out controller
+       for _, index := range c.Type.ControllerIndexes {
+               appController.FieldByIndex(index).Set(reflect.Zero(reflect.TypeOf(c.AppController).Elem().FieldByIndex(index).Type))
+       }
+}
+
+func findControllers(appControllerType reflect.Type) (indexes [][]int) {
+       // It might be a multi-level embedding. To find the controllers, we follow
+       // every anonymous field, using breadth-first search.
+       type nodeType struct {
+               val   reflect.Value
+               index []int
+       }
+       appControllerPtr := reflect.New(appControllerType)
+       queue := []nodeType{{appControllerPtr, []int{}}}
+       for len(queue) > 0 {
+               // Get the next value and de-reference it if necessary.
+               var (
+                       node     = queue[0]
+                       elem     = node.val
+                       elemType = elem.Type()
+               )
+               if elemType.Kind() == reflect.Ptr {
+                       elem = elem.Elem()
+                       elemType = elem.Type()
+               }
+               queue = queue[1:]
+
+               // #944 if the type's Kind is not `Struct` move on,
+               // otherwise `elem.NumField()` will panic
+               if elemType.Kind() != reflect.Struct {
+                       continue
+               }
+
+               // Look at all the struct fields.
+               for i := 0; i < elem.NumField(); i++ {
+                       // If this is not an anonymous field, skip it.
+                       structField := elemType.Field(i)
+                       if !structField.Anonymous {
+                               continue
+                       }
+
+                       fieldValue := elem.Field(i)
+                       fieldType := structField.Type
+
+                       // If it's a Controller, record the field indexes to get here.
+                       if fieldType == controllerPtrType {
+                               indexes = append(indexes, append(node.index, i))
+                               continue
+                       }
+
+                       queue = append(queue,
+                               nodeType{fieldValue, append(append([]int{}, node.index...), i)})
+               }
+       }
+       return
+}
+
+// RegisterController registers a Controller and its Methods with Revel.
+func RegisterController(c interface{}, methods []*MethodType) {
+       // De-star the controller type
+       // (e.g. given TypeOf((*Application)(nil)), want TypeOf(Application))
+       elem := reflect.TypeOf(c).Elem()
+
+       // De-star all of the method arg types too.
+       for _, m := range methods {
+               m.lowerName = strings.ToLower(m.Name)
+               for _, arg := range m.Args {
+                       arg.Type = arg.Type.Elem()
+               }
+       }
+
+       // Fetch module for controller, if none found controller must be part of the app
+       controllerModule := ModuleFromPath(elem.PkgPath(), true)
+
+       controllerType := AddControllerType(controllerModule, elem, methods)
+
+       controllerLog.Debug("RegisterController:Registered controller", "controller", controllerType.Name())
+}