func (n *node) addRoute(path string, handlers HandlersChain) { fullPath := path n.priority++
// Empty tree if len(n.path) == 0 && len(n.children) == 0 { n.insertChild(path, fullPath, handlers) n.nType = root return }
parentFullPathIndex := 0
walk: for { // Find the longest common prefix. // This also implies that the common prefix contains no ':' or '*' // since the existing key can't contain those chars. i := longestCommonPrefix(path, n.path)
// Split edge if i < len(n.path) { child := node{ path: n.path[i:], wildChild: n.wildChild, indices: n.indices, children: n.children, handlers: n.handlers, priority: n.priority - 1, fullPath: n.fullPath, }
n.children = []*node{&child} // []byte for proper unicode char conversion, see #65 n.indices = bytesconv.BytesToString([]byte{n.path[i]}) n.path = path[:i] n.handlers = nil n.wildChild = false n.fullPath = fullPath[:parentFullPathIndex+i] }
// Make new node a child of this node if i < len(path) { path = path[i:] c := path[0]
// '/' after param if n.nType == param && c == '/' && len(n.children) == 1 { parentFullPathIndex += len(n.path) n = n.children[0] n.priority++ continue walk }
// Check if a child with the next path byte exists for i, max := 0, len(n.indices); i < max; i++ { if c == n.indices[i] { parentFullPathIndex += len(n.path) i = n.incrementChildPrio(i) n = n.children[i] continue walk } }
// Otherwise insert it if c != ':' && c != '*' && n.nType != catchAll { // []byte for proper unicode char conversion, see #65 n.indices += bytesconv.BytesToString([]byte{c}) child := &node{ fullPath: fullPath, } n.addChild(child) n.incrementChildPrio(len(n.indices) - 1) n = child } else if n.wildChild { // inserting a wildcard node, need to check if it conflicts with the existing wildcard n = n.children[len(n.children)-1] n.priority++
// Check if the wildcard matches if len(path) >= len(n.path) && n.path == path[:len(n.path)] && // Adding a child to a catchAll is not possible n.nType != catchAll && // Check for longer wildcard, e.g. :name and :names (len(n.path) >= len(path) || path[len(n.path)] == '/') { continue walk }
// Wildcard conflict pathSeg := path if n.nType != catchAll { pathSeg = strings.SplitN(pathSeg, "/", 2)[0] } prefix := fullPath[:strings.Index(fullPath, pathSeg)] + n.path panic("'" + pathSeg + "' in new path '" + fullPath + "' conflicts with existing wildcard '" + n.path + "' in existing prefix '" + prefix + "'") }
n.insertChild(path, fullPath, handlers) return }
// Otherwise add handle to current node if n.handlers != nil { panic("handlers are already registered for path '" + fullPath + "'") } n.handlers = handlers n.fullPath = fullPath return }}