path_expression.go 2.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. package restful
  2. // Copyright 2013 Ernest Micklei. All rights reserved.
  3. // Use of this source code is governed by a license
  4. // that can be found in the LICENSE file.
  5. import (
  6. "bytes"
  7. "fmt"
  8. "regexp"
  9. "strings"
  10. )
  11. // PathExpression holds a compiled path expression (RegExp) needed to match against
  12. // Http request paths and to extract path parameter values.
  13. type pathExpression struct {
  14. LiteralCount int // the number of literal characters (means those not resulting from template variable substitution)
  15. VarNames []string // the names of parameters (enclosed by {}) in the path
  16. VarCount int // the number of named parameters (enclosed by {}) in the path
  17. Matcher *regexp.Regexp
  18. Source string // Path as defined by the RouteBuilder
  19. tokens []string
  20. }
  21. // NewPathExpression creates a PathExpression from the input URL path.
  22. // Returns an error if the path is invalid.
  23. func newPathExpression(path string) (*pathExpression, error) {
  24. expression, literalCount, varNames, varCount, tokens := templateToRegularExpression(path)
  25. compiled, err := regexp.Compile(expression)
  26. if err != nil {
  27. return nil, err
  28. }
  29. return &pathExpression{literalCount, varNames, varCount, compiled, expression, tokens}, nil
  30. }
  31. // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
  32. func templateToRegularExpression(template string) (expression string, literalCount int, varNames []string, varCount int, tokens []string) {
  33. var buffer bytes.Buffer
  34. buffer.WriteString("^")
  35. //tokens = strings.Split(template, "/")
  36. tokens = tokenizePath(template)
  37. for _, each := range tokens {
  38. if each == "" {
  39. continue
  40. }
  41. buffer.WriteString("/")
  42. if strings.HasPrefix(each, "{") {
  43. // check for regular expression in variable
  44. colon := strings.Index(each, ":")
  45. var varName string
  46. if colon != -1 {
  47. // extract expression
  48. varName = strings.TrimSpace(each[1:colon])
  49. paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
  50. if paramExpr == "*" { // special case
  51. buffer.WriteString("(.*)")
  52. } else {
  53. buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
  54. }
  55. } else {
  56. // plain var
  57. varName = strings.TrimSpace(each[1 : len(each)-1])
  58. buffer.WriteString("([^/]+?)")
  59. }
  60. varNames = append(varNames, varName)
  61. varCount += 1
  62. } else {
  63. literalCount += len(each)
  64. encoded := each // TODO URI encode
  65. buffer.WriteString(regexp.QuoteMeta(encoded))
  66. }
  67. }
  68. return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varNames, varCount, tokens
  69. }