548 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			548 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2014 beego Author. All Rights Reserved.
 | 
						|
//
 | 
						|
// Licensed under the Apache License, Version 2.0 (the "License");
 | 
						|
// you may not use this file except in compliance with the License.
 | 
						|
// You may obtain a copy of the License at
 | 
						|
//
 | 
						|
//      http://www.apache.org/licenses/LICENSE-2.0
 | 
						|
//
 | 
						|
// Unless required by applicable law or agreed to in writing, software
 | 
						|
// distributed under the License is distributed on an "AS IS" BASIS,
 | 
						|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
						|
// See the License for the specific language governing permissions and
 | 
						|
// limitations under the License.
 | 
						|
 | 
						|
package beego
 | 
						|
 | 
						|
import (
 | 
						|
	"path"
 | 
						|
	"regexp"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/astaxie/beego/utils"
 | 
						|
)
 | 
						|
 | 
						|
type Tree struct {
 | 
						|
	//search fix route first
 | 
						|
	fixrouters map[string]*Tree
 | 
						|
 | 
						|
	//if set, failure to match fixrouters search then search wildcard
 | 
						|
	wildcard *Tree
 | 
						|
 | 
						|
	//if set, failure to match wildcard search
 | 
						|
	leaves []*leafInfo
 | 
						|
}
 | 
						|
 | 
						|
func NewTree() *Tree {
 | 
						|
	return &Tree{
 | 
						|
		fixrouters: make(map[string]*Tree),
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// add Tree to the exist Tree
 | 
						|
// prefix should has no params
 | 
						|
func (t *Tree) AddTree(prefix string, tree *Tree) {
 | 
						|
	t.addtree(splitPath(prefix), tree, nil, "")
 | 
						|
}
 | 
						|
 | 
						|
func (t *Tree) addtree(segments []string, tree *Tree, wildcards []string, reg string) {
 | 
						|
	if len(segments) == 0 {
 | 
						|
		panic("prefix should has path")
 | 
						|
	}
 | 
						|
	seg := segments[0]
 | 
						|
	iswild, params, regexpStr := splitSegment(seg)
 | 
						|
	if len(segments) == 1 {
 | 
						|
		if iswild {
 | 
						|
			if regexpStr != "" {
 | 
						|
				if reg == "" {
 | 
						|
					rr := ""
 | 
						|
					for _, w := range wildcards {
 | 
						|
						if w == "." || w == ":" {
 | 
						|
							continue
 | 
						|
						}
 | 
						|
						if w == ":splat" {
 | 
						|
							rr = rr + "(.+)/"
 | 
						|
						} else {
 | 
						|
							rr = rr + "([^/]+)/"
 | 
						|
						}
 | 
						|
					}
 | 
						|
					regexpStr = rr + regexpStr
 | 
						|
				} else {
 | 
						|
					regexpStr = "/" + regexpStr
 | 
						|
				}
 | 
						|
			} else {
 | 
						|
				for _, w := range wildcards {
 | 
						|
					if w == "." || w == ":" {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					regexpStr = "([^/]+)/" + regexpStr
 | 
						|
				}
 | 
						|
			}
 | 
						|
			reg = strings.Trim(reg+regexpStr, "/")
 | 
						|
			filterTreeWithPrefix(tree, append(wildcards, params...), reg)
 | 
						|
			t.wildcard = tree
 | 
						|
		} else {
 | 
						|
			filterTreeWithPrefix(tree, append(wildcards, params...), reg)
 | 
						|
			t.fixrouters[seg] = tree
 | 
						|
		}
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if iswild {
 | 
						|
		if t.wildcard == nil {
 | 
						|
			t.wildcard = NewTree()
 | 
						|
		}
 | 
						|
		if regexpStr != "" {
 | 
						|
			if reg == "" {
 | 
						|
				rr := ""
 | 
						|
				for _, w := range wildcards {
 | 
						|
					if w == "." || w == ":" {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					if w == ":splat" {
 | 
						|
						rr = rr + "(.+)/"
 | 
						|
					} else {
 | 
						|
						rr = rr + "([^/]+)/"
 | 
						|
					}
 | 
						|
				}
 | 
						|
				regexpStr = rr + regexpStr + "/"
 | 
						|
			} else {
 | 
						|
				regexpStr = "/" + regexpStr + "/"
 | 
						|
			}
 | 
						|
		} else {
 | 
						|
			for _, w := range wildcards {
 | 
						|
				if w == "." || w == ":" {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if w == ":splat" {
 | 
						|
					regexpStr = "(.+)/" + regexpStr
 | 
						|
				} else {
 | 
						|
					regexpStr = "([^/]+)/" + regexpStr
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
		reg = reg + regexpStr
 | 
						|
		t.wildcard.addtree(segments[1:], tree, append(wildcards, params...), reg)
 | 
						|
	} else {
 | 
						|
		subTree := NewTree()
 | 
						|
		t.fixrouters[seg] = subTree
 | 
						|
		subTree.addtree(segments[1:], tree, append(wildcards, params...), reg)
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func filterTreeWithPrefix(t *Tree, wildcards []string, reg string) {
 | 
						|
	for _, v := range t.fixrouters {
 | 
						|
		filterTreeWithPrefix(v, wildcards, reg)
 | 
						|
	}
 | 
						|
	if t.wildcard != nil {
 | 
						|
		filterTreeWithPrefix(t.wildcard, wildcards, reg)
 | 
						|
	}
 | 
						|
	for _, l := range t.leaves {
 | 
						|
		if reg != "" {
 | 
						|
			if l.regexps != nil {
 | 
						|
				l.wildcards = append(wildcards, l.wildcards...)
 | 
						|
				l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$")
 | 
						|
			} else {
 | 
						|
				for _, v := range l.wildcards {
 | 
						|
					if v == ":" || v == "." {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					if v == ":splat" {
 | 
						|
						reg = reg + "/(.+)"
 | 
						|
					} else {
 | 
						|
						reg = reg + "/([^/]+)"
 | 
						|
					}
 | 
						|
				}
 | 
						|
				l.regexps = regexp.MustCompile("^" + reg + "$")
 | 
						|
				l.wildcards = append(wildcards, l.wildcards...)
 | 
						|
			}
 | 
						|
			filterCards := []string{}
 | 
						|
			for _, v := range l.wildcards {
 | 
						|
				if v == ":" || v == "." {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				filterCards = append(filterCards, v)
 | 
						|
			}
 | 
						|
			l.wildcards = filterCards
 | 
						|
		} else {
 | 
						|
			l.wildcards = append(wildcards, l.wildcards...)
 | 
						|
			if l.regexps != nil {
 | 
						|
				for _, w := range wildcards {
 | 
						|
					if w == "." || w == ":" {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					if w == ":splat" {
 | 
						|
						reg = "(.+)/" + reg
 | 
						|
					} else {
 | 
						|
						reg = "([^/]+)/" + reg
 | 
						|
					}
 | 
						|
				}
 | 
						|
				l.regexps = regexp.MustCompile("^" + reg + strings.Trim(l.regexps.String(), "^$") + "$")
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// call addseg function
 | 
						|
func (t *Tree) AddRouter(pattern string, runObject interface{}) {
 | 
						|
	t.addseg(splitPath(pattern), runObject, nil, "")
 | 
						|
}
 | 
						|
 | 
						|
// "/"
 | 
						|
// "admin" ->
 | 
						|
func (t *Tree) addseg(segments []string, route interface{}, wildcards []string, reg string) {
 | 
						|
	if len(segments) == 0 {
 | 
						|
		if reg != "" {
 | 
						|
			filterCards := []string{}
 | 
						|
			for _, v := range wildcards {
 | 
						|
				if v == ":" || v == "." {
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				filterCards = append(filterCards, v)
 | 
						|
			}
 | 
						|
			t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: filterCards, regexps: regexp.MustCompile("^" + reg + "$")})
 | 
						|
		} else {
 | 
						|
			t.leaves = append(t.leaves, &leafInfo{runObject: route, wildcards: wildcards})
 | 
						|
		}
 | 
						|
	} else {
 | 
						|
		seg := segments[0]
 | 
						|
		iswild, params, regexpStr := splitSegment(seg)
 | 
						|
		//for the router  /login/*/access match /login/2009/11/access
 | 
						|
		if !iswild && utils.InSlice(":splat", wildcards) {
 | 
						|
			iswild = true
 | 
						|
			regexpStr = seg
 | 
						|
		}
 | 
						|
		if seg == "*" && len(wildcards) > 0 && reg == "" {
 | 
						|
			iswild = true
 | 
						|
			regexpStr = "(.+)"
 | 
						|
		}
 | 
						|
		if iswild {
 | 
						|
			if t.wildcard == nil {
 | 
						|
				t.wildcard = NewTree()
 | 
						|
			}
 | 
						|
			if regexpStr != "" {
 | 
						|
				if reg == "" {
 | 
						|
					rr := ""
 | 
						|
					for _, w := range wildcards {
 | 
						|
						if w == "." || w == ":" {
 | 
						|
							continue
 | 
						|
						}
 | 
						|
						if w == ":splat" {
 | 
						|
							rr = rr + "(.+)/"
 | 
						|
						} else {
 | 
						|
							rr = rr + "([^/]+)/"
 | 
						|
						}
 | 
						|
					}
 | 
						|
					regexpStr = rr + regexpStr
 | 
						|
				} else {
 | 
						|
					regexpStr = "/" + regexpStr
 | 
						|
				}
 | 
						|
			} else if reg != "" {
 | 
						|
				if seg == "*.*" {
 | 
						|
					regexpStr = "/([^.]+).(.+)"
 | 
						|
				} else {
 | 
						|
					for _, w := range params {
 | 
						|
 | 
						|
						if w == "." || w == ":" {
 | 
						|
							continue
 | 
						|
						}
 | 
						|
						regexpStr = "/([^/]+)" + regexpStr
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
			}
 | 
						|
			t.wildcard.addseg(segments[1:], route, append(wildcards, params...), reg+regexpStr)
 | 
						|
		} else {
 | 
						|
			subTree, ok := t.fixrouters[seg]
 | 
						|
			if !ok {
 | 
						|
				subTree = NewTree()
 | 
						|
				t.fixrouters[seg] = subTree
 | 
						|
			}
 | 
						|
			subTree.addseg(segments[1:], route, wildcards, reg)
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// match router to runObject & params
 | 
						|
func (t *Tree) Match(pattern string) (runObject interface{}, params map[string]string) {
 | 
						|
	if len(pattern) == 0 || pattern[0] != '/' {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	return t.match(splitPath(pattern), nil)
 | 
						|
}
 | 
						|
 | 
						|
func (t *Tree) match(segments []string, wildcardValues []string) (runObject interface{}, params map[string]string) {
 | 
						|
	// Handle leaf nodes:
 | 
						|
	if len(segments) == 0 {
 | 
						|
		for _, l := range t.leaves {
 | 
						|
			if ok, pa := l.match(wildcardValues); ok {
 | 
						|
				return l.runObject, pa
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if t.wildcard != nil {
 | 
						|
			for _, l := range t.wildcard.leaves {
 | 
						|
				if ok, pa := l.match(wildcardValues); ok {
 | 
						|
					return l.runObject, pa
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
		}
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	seg, segs := segments[0], segments[1:]
 | 
						|
 | 
						|
	subTree, ok := t.fixrouters[seg]
 | 
						|
	if ok {
 | 
						|
		runObject, params = subTree.match(segs, wildcardValues)
 | 
						|
	} else if len(segs) == 0 { //.json .xml
 | 
						|
		if subindex := strings.LastIndex(seg, "."); subindex != -1 {
 | 
						|
			subTree, ok = t.fixrouters[seg[:subindex]]
 | 
						|
			if ok {
 | 
						|
				runObject, params = subTree.match(segs, wildcardValues)
 | 
						|
				if runObject != nil {
 | 
						|
					if params == nil {
 | 
						|
						params = make(map[string]string)
 | 
						|
					}
 | 
						|
					params[":ext"] = seg[subindex+1:]
 | 
						|
					return runObject, params
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if runObject == nil && t.wildcard != nil {
 | 
						|
		runObject, params = t.wildcard.match(segs, append(wildcardValues, seg))
 | 
						|
	}
 | 
						|
	if runObject == nil {
 | 
						|
		for _, l := range t.leaves {
 | 
						|
			if ok, pa := l.match(append(wildcardValues, segments...)); ok {
 | 
						|
				return l.runObject, pa
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return runObject, params
 | 
						|
}
 | 
						|
 | 
						|
type leafInfo struct {
 | 
						|
	// names of wildcards that lead to this leaf. eg, ["id" "name"] for the wildcard ":id" and ":name"
 | 
						|
	wildcards []string
 | 
						|
 | 
						|
	// if the leaf is regexp
 | 
						|
	regexps *regexp.Regexp
 | 
						|
 | 
						|
	runObject interface{}
 | 
						|
}
 | 
						|
 | 
						|
func (leaf *leafInfo) match(wildcardValues []string) (ok bool, params map[string]string) {
 | 
						|
	if leaf.regexps == nil {
 | 
						|
		// has error
 | 
						|
		if len(wildcardValues) == 0 && len(leaf.wildcards) > 0 {
 | 
						|
			if utils.InSlice(":", leaf.wildcards) {
 | 
						|
				params = make(map[string]string)
 | 
						|
				j := 0
 | 
						|
				for _, v := range leaf.wildcards {
 | 
						|
					if v == ":" {
 | 
						|
						continue
 | 
						|
					}
 | 
						|
					params[v] = ""
 | 
						|
					j += 1
 | 
						|
				}
 | 
						|
				return true, params
 | 
						|
			}
 | 
						|
			return false, nil
 | 
						|
		} else if len(wildcardValues) == 0 { // static path
 | 
						|
			return true, nil
 | 
						|
		}
 | 
						|
		// match *
 | 
						|
		if len(leaf.wildcards) == 1 && leaf.wildcards[0] == ":splat" {
 | 
						|
			params = make(map[string]string)
 | 
						|
			params[":splat"] = path.Join(wildcardValues...)
 | 
						|
			return true, params
 | 
						|
		}
 | 
						|
		// match *.*
 | 
						|
		if len(leaf.wildcards) == 3 && leaf.wildcards[0] == "." {
 | 
						|
			params = make(map[string]string)
 | 
						|
			lastone := wildcardValues[len(wildcardValues)-1]
 | 
						|
			strs := strings.SplitN(lastone, ".", 2)
 | 
						|
			if len(strs) == 2 {
 | 
						|
				params[":ext"] = strs[1]
 | 
						|
			} else {
 | 
						|
				params[":ext"] = ""
 | 
						|
			}
 | 
						|
			params[":path"] = path.Join(wildcardValues[:len(wildcardValues)-1]...) + "/" + strs[0]
 | 
						|
			return true, params
 | 
						|
		}
 | 
						|
		// match :id
 | 
						|
		params = make(map[string]string)
 | 
						|
		j := 0
 | 
						|
		for _, v := range leaf.wildcards {
 | 
						|
			if v == ":" {
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if v == "." {
 | 
						|
				lastone := wildcardValues[len(wildcardValues)-1]
 | 
						|
				strs := strings.SplitN(lastone, ".", 2)
 | 
						|
				if len(strs) == 2 {
 | 
						|
					params[":ext"] = strs[1]
 | 
						|
				} else {
 | 
						|
					params[":ext"] = ""
 | 
						|
				}
 | 
						|
				if len(wildcardValues[j:]) == 1 {
 | 
						|
					params[":path"] = strs[0]
 | 
						|
				} else {
 | 
						|
					params[":path"] = path.Join(wildcardValues[j:]...) + "/" + strs[0]
 | 
						|
				}
 | 
						|
				return true, params
 | 
						|
			}
 | 
						|
			if len(wildcardValues) <= j {
 | 
						|
				return false, nil
 | 
						|
			}
 | 
						|
			params[v] = wildcardValues[j]
 | 
						|
			j += 1
 | 
						|
		}
 | 
						|
		if len(params) != len(wildcardValues) {
 | 
						|
			return false, nil
 | 
						|
		}
 | 
						|
		return true, params
 | 
						|
	}
 | 
						|
 | 
						|
	if !leaf.regexps.MatchString(path.Join(wildcardValues...)) {
 | 
						|
		return false, nil
 | 
						|
	}
 | 
						|
	params = make(map[string]string)
 | 
						|
	matches := leaf.regexps.FindStringSubmatch(path.Join(wildcardValues...))
 | 
						|
	for i, match := range matches[1:] {
 | 
						|
		params[leaf.wildcards[i]] = match
 | 
						|
	}
 | 
						|
	return true, params
 | 
						|
}
 | 
						|
 | 
						|
// "/" -> []
 | 
						|
// "/admin" -> ["admin"]
 | 
						|
// "/admin/" -> ["admin"]
 | 
						|
// "/admin/users" -> ["admin", "users"]
 | 
						|
func splitPath(key string) []string {
 | 
						|
	elements := strings.Split(key, "/")
 | 
						|
	if elements[0] == "" {
 | 
						|
		elements = elements[1:]
 | 
						|
	}
 | 
						|
	if elements[len(elements)-1] == "" {
 | 
						|
		elements = elements[:len(elements)-1]
 | 
						|
	}
 | 
						|
	return elements
 | 
						|
}
 | 
						|
 | 
						|
// "admin" -> false, nil, ""
 | 
						|
// ":id" -> true, [:id], ""
 | 
						|
// "?:id" -> true, [: :id], ""        : meaning can empty
 | 
						|
// ":id:int" -> true, [:id], ([0-9]+)
 | 
						|
// ":name:string" -> true, [:name], ([\w]+)
 | 
						|
// ":id([0-9]+)" -> true, [:id], ([0-9]+)
 | 
						|
// ":id([0-9]+)_:name" -> true, [:id :name], ([0-9]+)_(.+)
 | 
						|
// "cms_:id_:page.html" -> true, [:id :page], cms_(.+)_(.+).html
 | 
						|
// "*" -> true, [:splat], ""
 | 
						|
// "*.*" -> true,[. :path :ext], ""      . meaning separator
 | 
						|
func splitSegment(key string) (bool, []string, string) {
 | 
						|
	if strings.HasPrefix(key, "*") {
 | 
						|
		if key == "*.*" {
 | 
						|
			return true, []string{".", ":path", ":ext"}, ""
 | 
						|
		} else {
 | 
						|
			return true, []string{":splat"}, ""
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if strings.ContainsAny(key, ":") {
 | 
						|
		var paramsNum int
 | 
						|
		var out []rune
 | 
						|
		var start bool
 | 
						|
		var startexp bool
 | 
						|
		var param []rune
 | 
						|
		var expt []rune
 | 
						|
		var skipnum int
 | 
						|
		params := []string{}
 | 
						|
		reg := regexp.MustCompile(`[a-zA-Z0-9_]+`)
 | 
						|
		for i, v := range key {
 | 
						|
			if skipnum > 0 {
 | 
						|
				skipnum -= 1
 | 
						|
				continue
 | 
						|
			}
 | 
						|
			if start {
 | 
						|
				//:id:int and :name:string
 | 
						|
				if v == ':' {
 | 
						|
					if len(key) >= i+4 {
 | 
						|
						if key[i+1:i+4] == "int" {
 | 
						|
							out = append(out, []rune("([0-9]+)")...)
 | 
						|
							params = append(params, ":"+string(param))
 | 
						|
							start = false
 | 
						|
							startexp = false
 | 
						|
							skipnum = 3
 | 
						|
							param = make([]rune, 0)
 | 
						|
							paramsNum += 1
 | 
						|
							continue
 | 
						|
						}
 | 
						|
					}
 | 
						|
					if len(key) >= i+7 {
 | 
						|
						if key[i+1:i+7] == "string" {
 | 
						|
							out = append(out, []rune(`([\w]+)`)...)
 | 
						|
							params = append(params, ":"+string(param))
 | 
						|
							paramsNum += 1
 | 
						|
							start = false
 | 
						|
							startexp = false
 | 
						|
							skipnum = 6
 | 
						|
							param = make([]rune, 0)
 | 
						|
							continue
 | 
						|
						}
 | 
						|
					}
 | 
						|
				}
 | 
						|
				// params only support a-zA-Z0-9
 | 
						|
				if reg.MatchString(string(v)) {
 | 
						|
					param = append(param, v)
 | 
						|
					continue
 | 
						|
				}
 | 
						|
				if v != '(' {
 | 
						|
					out = append(out, []rune(`(.+)`)...)
 | 
						|
					params = append(params, ":"+string(param))
 | 
						|
					param = make([]rune, 0)
 | 
						|
					paramsNum += 1
 | 
						|
					start = false
 | 
						|
					startexp = false
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if startexp {
 | 
						|
				if v != ')' {
 | 
						|
					expt = append(expt, v)
 | 
						|
					continue
 | 
						|
				}
 | 
						|
			}
 | 
						|
			if v == ':' {
 | 
						|
				param = make([]rune, 0)
 | 
						|
				start = true
 | 
						|
			} else if v == '(' {
 | 
						|
				startexp = true
 | 
						|
				start = false
 | 
						|
				params = append(params, ":"+string(param))
 | 
						|
				paramsNum += 1
 | 
						|
				expt = make([]rune, 0)
 | 
						|
				expt = append(expt, '(')
 | 
						|
			} else if v == ')' {
 | 
						|
				startexp = false
 | 
						|
				expt = append(expt, ')')
 | 
						|
				out = append(out, expt...)
 | 
						|
				param = make([]rune, 0)
 | 
						|
			} else if v == '?' {
 | 
						|
				params = append(params, ":")
 | 
						|
			} else {
 | 
						|
				out = append(out, v)
 | 
						|
			}
 | 
						|
		}
 | 
						|
		if len(param) > 0 {
 | 
						|
			if paramsNum > 0 {
 | 
						|
				out = append(out, []rune(`(.+)`)...)
 | 
						|
			}
 | 
						|
			params = append(params, ":"+string(param))
 | 
						|
		}
 | 
						|
		return true, params, string(out)
 | 
						|
	} else {
 | 
						|
		return false, nil, ""
 | 
						|
	}
 | 
						|
}
 |