path_expression.go 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  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. VarCount int // the number of named parameters (enclosed by {}) in the path
  16. Matcher *regexp.Regexp
  17. Source string // Path as defined by the RouteBuilder
  18. tokens []string
  19. }
  20. // NewPathExpression creates a PathExpression from the input URL path.
  21. // Returns an error if the path is invalid.
  22. func newPathExpression(path string) (*pathExpression, error) {
  23. expression, literalCount, varCount, tokens := templateToRegularExpression(path)
  24. compiled, err := regexp.Compile(expression)
  25. if err != nil {
  26. return nil, err
  27. }
  28. return &pathExpression{literalCount, varCount, compiled, expression, tokens}, nil
  29. }
  30. // http://jsr311.java.net/nonav/releases/1.1/spec/spec3.html#x3-370003.7.3
  31. func templateToRegularExpression(template string) (expression string, literalCount int, varCount int, tokens []string) {
  32. var buffer bytes.Buffer
  33. buffer.WriteString("^")
  34. //tokens = strings.Split(template, "/")
  35. tokens = tokenizePath(template)
  36. for _, each := range tokens {
  37. if each == "" {
  38. continue
  39. }
  40. buffer.WriteString("/")
  41. if strings.HasPrefix(each, "{") {
  42. // check for regular expression in variable
  43. colon := strings.Index(each, ":")
  44. if colon != -1 {
  45. // extract expression
  46. paramExpr := strings.TrimSpace(each[colon+1 : len(each)-1])
  47. if paramExpr == "*" { // special case
  48. buffer.WriteString("(.*)")
  49. } else {
  50. buffer.WriteString(fmt.Sprintf("(%s)", paramExpr)) // between colon and closing moustache
  51. }
  52. } else {
  53. // plain var
  54. buffer.WriteString("([^/]+?)")
  55. }
  56. varCount += 1
  57. } else {
  58. literalCount += len(each)
  59. encoded := each // TODO URI encode
  60. buffer.WriteString(regexp.QuoteMeta(encoded))
  61. }
  62. }
  63. return strings.TrimRight(buffer.String(), "/") + "(/.*)?$", literalCount, varCount, tokens
  64. }