github.com/astaxie/beego@v1.12.3/tree.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package beego 16 17 import ( 18 "path" 19 "regexp" 20 "strings" 21 22 "github.com/astaxie/beego/context" 23 "github.com/astaxie/beego/utils" 24 ) 25 26 var ( 27 allowSuffixExt = []string{".json", ".xml", ".html"} 28 ) 29 30 // Tree has three elements: FixRouter/wildcard/leaves 31 // fixRouter stores Fixed Router 32 // wildcard stores params 33 // leaves store the endpoint information 34 type Tree struct { 35 //prefix set for static router 36 prefix string 37 //search fix route first 38 fixrouters []*Tree 39 //if set, failure to match fixrouters search then search wildcard 40 wildcard *Tree 41 //if set, failure to match wildcard search 42 leaves []*leafInfo 43 } 44 45 // NewTree return a new Tree 46 func NewTree() *Tree { 47 return &Tree{} 48 } 49 50 // AddTree will add tree to the exist Tree 51 // prefix should has no params 52 func (t *Tree) AddTree(prefix string, tree *Tree) { 53 t.addtree(splitPath(prefix), tree, nil, "") 54 } 55 56 func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) { 57 if len(segments) == 0 { 58 panic("prefix should has path") 59 } 60 seg := segments[0] 61 iswild, params, regexpStr := splitSegment(seg) 62 // if it's ? meaning can igone this, so add one more rule for it 63 if len(params) > 0 && params[0] == ":" { 64 params = params[1:] 65 if len(segments[1:]) > 0 { 66 t.addtree(segments[1:], tree, append(wildcards, params...), reg) 67 } else { 68 filterTreeWithPrefix(tree, wildcards, reg) 69 } 70 } 71 //Rule: /login/*/access match /login/2009/11/access 72 //if already has *, and when loop the access, should as a regexpStr 73 if !iswild && utils.InSlice(":splat", wildcards) { 74 iswild = true 75 regexpStr = seg 76 } 77 //Rule: /user/:id/* 78 if seg == "*" && len(wildcards) > 0 && reg == "" { 79 regexpStr = "(.+)" 80 } 81 if len(segments) == 1 { 82 if iswild { 83 if regexpStr != "" { 84 if reg == "" { 85 rr := "" 86 for _, w := range wildcards { 87 if w == ":splat" { 88 rr = rr + "(.+)/" 89 } else { 90 rr = rr + "([^/]+)/" 91 } 92 } 93 regexpStr = rr + regexpStr 94 } else { 95 regexpStr = "/" + regexpStr 96 } 97 } else if reg != "" { 98 if seg == "*.*" { 99 regexpStr = "([^.]+).(.+)" 100 } else { 101 for _, w := range params { 102 if w == "." || w == ":" { 103 continue 104 } 105 regexpStr = "([^/]+)/" + regexpStr 106 } 107 } 108 } 109 reg = strings.Trim(reg+"/"+regexpStr, "/") 110 filterTreeWithPrefix(tree, append(wildcards, params...), reg) 111 t.wildcard = tree 112 } else { 113 reg = strings.Trim(reg+"/"+regexpStr, "/") 114 filterTreeWithPrefix(tree, append(wildcards, params...), reg) 115 tree.prefix = seg 116 t.fixrouters = append(t.fixrouters, tree) 117 } 118 return 119 } 120 121 if iswild { 122 if t.wildcard == nil { 123 t.wildcard = NewTree() 124 } 125 if regexpStr != "" { 126 if reg == "" { 127 rr := "" 128 for _, w := range wildcards { 129 if w == ":splat" { 130 rr = rr + "(.+)/" 131 } else { 132 rr = rr + "([^/]+)/" 133 } 134 } 135 regexpStr = rr + regexpStr 136 } else { 137 regexpStr = "/" + regexpStr 138 } 139 } else if reg != "" { 140 if seg == "*.*" { 141 regexpStr = "([^.]+).(.+)" 142 params = params[1:] 143 } else { 144 for range params { 145 regexpStr = "([^/]+)/" + regexpStr 146 } 147 } 148 } else { 149 if seg == "*.*" { 150 params = params[1:] 151 } 152 } 153 reg = strings.TrimRight(strings.TrimRight(reg, "/")+"/"+regexpStr, "/") 154 t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg) 155 } else { 156 subTree := NewTree() 157 subTree.prefix = seg 158 t.fixrouters = append(t.fixrouters, subTree) 159 subTree.addtree(segments[1:], tree, append(wildcards, params...), reg) 160 } 161 } 162 163 func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) { 164 for _, v := range t.fixrouters { 165 filterTreeWithPrefix(v, wildcards, reg) 166 } 167 if t.wildcard != nil { 168 filterTreeWithPrefix(t.wildcard, wildcards, reg) 169 } 170 for _, l := range t.leaves { 171 if reg != "" { 172 if l.regexps != nil { 173 l.wildcards = append(wildcards, l.wildcards...) 174 l.regexps = regexp.MustCompile("^" + reg + "/" + strings.Trim(l.regexps.String(), "^$") + "$") 175 } else { 176 for _, v := range l.wildcards { 177 if v == ":splat" { 178 reg = reg + "/(.+)" 179 } else { 180 reg = reg + "/([^/]+)" 181 } 182 } 183 l.regexps = regexp.MustCompile("^" + reg + "$") 184 l.wildcards = append(wildcards, l.wildcards...) 185 } 186 } else { 187 l.wildcards = append(wildcards, l.wildcards...) 188 if l.regexps != nil { 189 for _, w := range wildcards { 190 if w == ":splat" { 191 reg = "(.+)/" + reg 192 } else { 193 reg = "([^/]+)/" + reg 194 } 195 } 196 l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$") 197 } 198 } 199 } 200 } 201 202 // AddRouter call addseg function 203 func (t *Tree) AddRouter(pattern string, runObject interface{}) { 204 t.addseg(splitPath(pattern), runObject, nil, "") 205 } 206 207 // "/" 208 // "admin" -> 209 func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) { 210 if len(segments) == 0 { 211 if reg != "" { 212 t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards, regexps: regexp.MustCompile("^" + reg + "$")}) 213 } else { 214 t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards}) 215 } 216 } else { 217 seg := segments[0] 218 iswild, params, regexpStr := splitSegment(seg) 219 // if it's ? meaning can igone this, so add one more rule for it 220 if len(params) > 0 && params[0] == ":" { 221 t.addseg(segments[1:], route, wildcards, reg) 222 params = params[1:] 223 } 224 //Rule: /login/*/access match /login/2009/11/access 225 //if already has *, and when loop the access, should as a regexpStr 226 if !iswild && utils.InSlice(":splat", wildcards) { 227 iswild = true 228 regexpStr = seg 229 } 230 //Rule: /user/:id/* 231 if seg == "*" && len(wildcards) > 0 && reg == "" { 232 regexpStr = "(.+)" 233 } 234 if iswild { 235 if t.wildcard == nil { 236 t.wildcard = NewTree() 237 } 238 if regexpStr != "" { 239 if reg == "" { 240 rr := "" 241 for _, w := range wildcards { 242 if w == ":splat" { 243 rr = rr + "(.+)/" 244 } else { 245 rr = rr + "([^/]+)/" 246 } 247 } 248 regexpStr = rr + regexpStr 249 } else { 250 regexpStr = "/" + regexpStr 251 } 252 } else if reg != "" { 253 if seg == "*.*" { 254 regexpStr = "/([^.]+).(.+)" 255 params = params[1:] 256 } else { 257 for range params { 258 regexpStr = "/([^/]+)" + regexpStr 259 } 260 } 261 } else { 262 if seg == "*.*" { 263 params = params[1:] 264 } 265 } 266 t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr) 267 } else { 268 var subTree *Tree 269 for _, sub := range t.fixrouters { 270 if sub.prefix == seg { 271 subTree = sub 272 break 273 } 274 } 275 if subTree == nil { 276 subTree = NewTree() 277 subTree.prefix = seg 278 t.fixrouters = append(t.fixrouters, subTree) 279 } 280 subTree.addseg(segments[1:], route, wildcards, reg) 281 } 282 } 283 } 284 285 // Match router to runObject & params 286 func (t *Tree) Match(pattern string, ctx *context.Context) (runObject interface{}) { 287 if len(pattern) == 0 || pattern[0] != '/' { 288 return nil 289 } 290 w := make([]string, 0, 20) 291 return t.match(pattern[1:], pattern, w, ctx) 292 } 293 294 func (t *Tree) match(treePattern string, pattern string, wildcardValues []string, ctx *context.Context) (runObject interface{}) { 295 if len(pattern) > 0 { 296 i := 0 297 for ; i < len(pattern) && pattern[i] == '/'; i++ { 298 } 299 pattern = pattern[i:] 300 } 301 // Handle leaf nodes: 302 if len(pattern) == 0 { 303 for _, l := range t.leaves { 304 if ok := l.match(treePattern, wildcardValues, ctx); ok { 305 return l.runObject 306 } 307 } 308 if t.wildcard != nil { 309 for _, l := range t.wildcard.leaves { 310 if ok := l.match(treePattern, wildcardValues, ctx); ok { 311 return l.runObject 312 } 313 } 314 } 315 return nil 316 } 317 var seg string 318 i, l := 0, len(pattern) 319 for ; i < l && pattern[i] != '/'; i++ { 320 } 321 if i == 0 { 322 seg = pattern 323 pattern = "" 324 } else { 325 seg = pattern[:i] 326 pattern = pattern[i:] 327 } 328 for _, subTree := range t.fixrouters { 329 if subTree.prefix == seg { 330 if len(pattern) != 0 && pattern[0] == '/' { 331 treePattern = pattern[1:] 332 } else { 333 treePattern = pattern 334 } 335 runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) 336 if runObject != nil { 337 break 338 } 339 } 340 } 341 if runObject == nil && len(t.fixrouters) > 0 { 342 // Filter the .json .xml .html extension 343 for _, str := range allowSuffixExt { 344 if strings.HasSuffix(seg, str) { 345 for _, subTree := range t.fixrouters { 346 if subTree.prefix == seg[:len(seg)-len(str)] { 347 runObject = subTree.match(treePattern, pattern, wildcardValues, ctx) 348 if runObject != nil { 349 ctx.Input.SetParam(":ext", str[1:]) 350 } 351 } 352 } 353 } 354 } 355 } 356 if runObject == nil && t.wildcard != nil { 357 runObject = t.wildcard.match(treePattern, pattern, append(wildcardValues, seg), ctx) 358 } 359 360 if runObject == nil && len(t.leaves) > 0 { 361 wildcardValues = append(wildcardValues, seg) 362 start, i := 0, 0 363 for ; i < len(pattern); i++ { 364 if pattern[i] == '/' { 365 if i != 0 && start < len(pattern) { 366 wildcardValues = append(wildcardValues, pattern[start:i]) 367 } 368 start = i + 1 369 continue 370 } 371 } 372 if start > 0 { 373 wildcardValues = append(wildcardValues, pattern[start:i]) 374 } 375 for _, l := range t.leaves { 376 if ok := l.match(treePattern, wildcardValues, ctx); ok { 377 return l.runObject 378 } 379 } 380 } 381 return runObject 382 } 383 384 type leafInfo struct { 385 // names of wildcards that lead to this leaf. eg, ["id" "name"] for the wildcard ":id" and ":name" 386 wildcards []string 387 388 // if the leaf is regexp 389 regexps *regexp.Regexp 390 391 runObject interface{} 392 } 393 394 func (leaf *leafInfo) match(treePattern string, wildcardValues []string, ctx *context.Context) (ok bool) { 395 //fmt.Println("Leaf:", wildcardValues, leaf.wildcards, leaf.regexps) 396 if leaf.regexps == nil { 397 if len(wildcardValues) == 0 && len(leaf.wildcards) == 0 { // static path 398 return true 399 } 400 // match * 401 if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" { 402 ctx.Input.SetParam(":splat", treePattern) 403 return true 404 } 405 // match *.* or :id 406 if len(leaf.wildcards) >= 2 && leaf.wildcards[len(leaf.wildcards)-2] == ":path" && leaf.wildcards[len(leaf.wildcards)-1] == ":ext" { 407 if len(leaf.wildcards) == 2 { 408 lastone := wildcardValues[len(wildcardValues)-1] 409 strs := strings.SplitN(lastone, ".", 2) 410 if len(strs) == 2 { 411 ctx.Input.SetParam(":ext", strs[1]) 412 } 413 ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[:len(wildcardValues)-1]...), strs[0])) 414 return true 415 } else if len(wildcardValues) < 2 { 416 return false 417 } 418 var index int 419 for index = 0; index < len(leaf.wildcards)-2; index++ { 420 ctx.Input.SetParam(leaf.wildcards[index], wildcardValues[index]) 421 } 422 lastone := wildcardValues[len(wildcardValues)-1] 423 strs := strings.SplitN(lastone, ".", 2) 424 if len(strs) == 2 { 425 ctx.Input.SetParam(":ext", strs[1]) 426 } 427 if index > (len(wildcardValues) - 1) { 428 ctx.Input.SetParam(":path", "") 429 } else { 430 ctx.Input.SetParam(":path", path.Join(path.Join(wildcardValues[index:len(wildcardValues)-1]...), strs[0])) 431 } 432 return true 433 } 434 // match :id 435 if len(leaf.wildcards) != len(wildcardValues) { 436 return false 437 } 438 for j, v := range leaf.wildcards { 439 ctx.Input.SetParam(v, wildcardValues[j]) 440 } 441 return true 442 } 443 444 if !leaf.regexps.MatchString(path.Join(wildcardValues...)) { 445 return false 446 } 447 matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...)) 448 for i, match := range matches[1:] { 449 if i < len(leaf.wildcards) { 450 ctx.Input.SetParam(leaf.wildcards[i], match) 451 } 452 } 453 return true 454 } 455 456 // "/" -> [] 457 // "/admin" -> ["admin"] 458 // "/admin/" -> ["admin"] 459 // "/admin/users" -> ["admin", "users"] 460 func splitPath(key string) []string { 461 key = strings.Trim(key, "/ ") 462 if key == "" { 463 return []string{} 464 } 465 return strings.Split(key, "/") 466 } 467 468 // "admin" -> false, nil, "" 469 // ":id" -> true, [:id], "" 470 // "?:id" -> true, [: :id], "" : meaning can empty 471 // ":id:int" -> true, [:id], ([0-9]+) 472 // ":name:string" -> true, [:name], ([\w]+) 473 // ":id([0-9]+)" -> true, [:id], ([0-9]+) 474 // ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+) 475 // "cms_:id_:page.html" -> true, [:id_ :page], cms_(.+)(.+).html 476 // "cms_:id(.+)_:page.html" -> true, [:id :page], cms_(.+)_(.+).html 477 // "*" -> true, [:splat], "" 478 // "*.*" -> true,[. :path :ext], "" . meaning separator 479 func splitSegment(key string) (bool, []string, string) { 480 if strings.HasPrefix(key, "*") { 481 if key == "*.*" { 482 return true, []string{".", ":path", ":ext"}, "" 483 } 484 return true, []string{":splat"}, "" 485 } 486 if strings.ContainsAny(key, ":") { 487 var paramsNum int 488 var out []rune 489 var start bool 490 var startexp bool 491 var param []rune 492 var expt []rune 493 var skipnum int 494 params := []string{} 495 reg := regexp.MustCompile(`[a-zA-Z0-9_]+`) 496 for i, v := range key { 497 if skipnum > 0 { 498 skipnum-- 499 continue 500 } 501 if start { 502 //:id:int and :name:string 503 if v == ':' { 504 if len(key) >= i+4 { 505 if key[i+1:i+4] == "int" { 506 out = append(out, []rune("([0-9]+)")...) 507 params = append(params, ":"+string(param)) 508 start = false 509 startexp = false 510 skipnum = 3 511 param = make([]rune, 0) 512 paramsNum++ 513 continue 514 } 515 } 516 if len(key) >= i+7 { 517 if key[i+1:i+7] == "string" { 518 out = append(out, []rune(`([\w]+)`)...) 519 params = append(params, ":"+string(param)) 520 paramsNum++ 521 start = false 522 startexp = false 523 skipnum = 6 524 param = make([]rune, 0) 525 continue 526 } 527 } 528 } 529 // params only support a-zA-Z0-9 530 if reg.MatchString(string(v)) { 531 param = append(param, v) 532 continue 533 } 534 if v != '(' { 535 out = append(out, []rune(`(.+)`)...) 536 params = append(params, ":"+string(param)) 537 param = make([]rune, 0) 538 paramsNum++ 539 start = false 540 startexp = false 541 } 542 } 543 if startexp { 544 if v != ')' { 545 expt = append(expt, v) 546 continue 547 } 548 } 549 // Escape Sequence '\' 550 if i > 0 && key[i-1] == '\\' { 551 out = append(out, v) 552 } else if v == ':' { 553 param = make([]rune, 0) 554 start = true 555 } else if v == '(' { 556 startexp = true 557 start = false 558 if len(param) > 0 { 559 params = append(params, ":"+string(param)) 560 param = make([]rune, 0) 561 } 562 paramsNum++ 563 expt = make([]rune, 0) 564 expt = append(expt, '(') 565 } else if v == ')' { 566 startexp = false 567 expt = append(expt, ')') 568 out = append(out, expt...) 569 param = make([]rune, 0) 570 } else if v == '?' { 571 params = append(params, ":") 572 } else { 573 out = append(out, v) 574 } 575 } 576 if len(param) > 0 { 577 if paramsNum > 0 { 578 out = append(out, []rune(`(.+)`)...) 579 } 580 params = append(params, ":"+string(param)) 581 } 582 return true, params, string(out) 583 } 584 return false, nil, "" 585 }