summaryrefslogtreecommitdiff
path: root/src/cmd/godoc/mapping.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/godoc/mapping.go')
-rw-r--r--src/cmd/godoc/mapping.go200
1 files changed, 200 insertions, 0 deletions
diff --git a/src/cmd/godoc/mapping.go b/src/cmd/godoc/mapping.go
new file mode 100644
index 000000000..51f23ab98
--- /dev/null
+++ b/src/cmd/godoc/mapping.go
@@ -0,0 +1,200 @@
+// Copyright 2009 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This file implements the Mapping data structure.
+
+package main
+
+import (
+ "fmt"
+ "io"
+ "path"
+ "path/filepath"
+ "sort"
+ "strings"
+)
+
+// A Mapping object maps relative paths (e.g. from URLs)
+// to absolute paths (of the file system) and vice versa.
+//
+// A Mapping object consists of a list of individual mappings
+// of the form: prefix -> path which are interpreted as follows:
+// A relative path of the form prefix/tail is to be mapped to
+// the absolute path/tail, if that absolute path exists in the file
+// system. Given a Mapping object, a relative path is mapped to an
+// absolute path by trying each of the individual mappings in order,
+// until a valid mapping is found. For instance, for the mapping:
+//
+// user -> /home/user
+// public -> /home/user/public
+// public -> /home/build/public
+//
+// the relative paths below are mapped to absolute paths as follows:
+//
+// user/foo -> /home/user/foo
+// public/net/rpc/file1.go -> /home/user/public/net/rpc/file1.go
+//
+// If there is no /home/user/public/net/rpc/file2.go, the next public
+// mapping entry is used to map the relative path to:
+//
+// public/net/rpc/file2.go -> /home/build/public/net/rpc/file2.go
+//
+// (assuming that file exists).
+//
+// Each individual mapping also has a RWValue associated with it that
+// may be used to store mapping-specific information. See the Iterate
+// method.
+//
+type Mapping struct {
+ list []mapping
+ prefixes []string // lazily computed from list
+}
+
+type mapping struct {
+ prefix, path string
+ value *RWValue
+}
+
+// Init initializes the Mapping from a list of paths.
+// Empty paths are ignored; relative paths are assumed to be relative to
+// the current working directory and converted to absolute paths.
+// For each path of the form:
+//
+// dirname/localname
+//
+// a mapping
+//
+// localname -> path
+//
+// is added to the Mapping object, in the order of occurrence.
+// For instance, under Unix, the argument:
+//
+// /home/user:/home/build/public
+//
+// leads to the following mapping:
+//
+// user -> /home/user
+// public -> /home/build/public
+//
+func (m *Mapping) Init(paths []string) {
+ pathlist := canonicalizePaths(paths, nil)
+ list := make([]mapping, len(pathlist))
+
+ // create mapping list
+ for i, path := range pathlist {
+ _, prefix := filepath.Split(path)
+ list[i] = mapping{prefix, path, new(RWValue)}
+ }
+
+ m.list = list
+}
+
+// IsEmpty returns true if there are no mappings specified.
+func (m *Mapping) IsEmpty() bool { return len(m.list) == 0 }
+
+// PrefixList returns a list of all prefixes, with duplicates removed.
+// For instance, for the mapping:
+//
+// user -> /home/user
+// public -> /home/user/public
+// public -> /home/build/public
+//
+// the prefix list is:
+//
+// user, public
+//
+func (m *Mapping) PrefixList() []string {
+ // compute the list lazily
+ if m.prefixes == nil {
+ list := make([]string, len(m.list))
+
+ // populate list
+ for i, e := range m.list {
+ list[i] = e.prefix
+ }
+
+ // sort the list and remove duplicate entries
+ sort.Strings(list)
+ i := 0
+ prev := ""
+ for _, path := range list {
+ if path != prev {
+ list[i] = path
+ i++
+ prev = path
+ }
+ }
+
+ m.prefixes = list[0:i]
+ }
+
+ return m.prefixes
+}
+
+// Fprint prints the mapping.
+func (m *Mapping) Fprint(w io.Writer) {
+ for _, e := range m.list {
+ fmt.Fprintf(w, "\t%s -> %s\n", e.prefix, e.path)
+ }
+}
+
+func splitFirst(path string) (head, tail string) {
+ i := strings.Index(path, string(filepath.Separator))
+ if i > 0 {
+ // 0 < i < len(path)
+ return path[0:i], path[i+1:]
+ }
+ return "", path
+}
+
+// ToAbsolute maps a slash-separated relative path to an absolute filesystem
+// path using the Mapping specified by the receiver. If the path cannot
+// be mapped, the empty string is returned.
+//
+func (m *Mapping) ToAbsolute(spath string) string {
+ fpath := filepath.FromSlash(spath)
+ prefix, tail := splitFirst(fpath)
+ for _, e := range m.list {
+ switch {
+ case e.prefix == prefix:
+ // use tail
+ case e.prefix == "":
+ tail = fpath
+ default:
+ continue // no match
+ }
+ abspath := filepath.Join(e.path, tail)
+ if _, err := fs.Stat(abspath); err == nil {
+ return abspath
+ }
+ }
+
+ return "" // no match
+}
+
+// ToRelative maps an absolute filesystem path to a relative slash-separated
+// path using the Mapping specified by the receiver. If the path cannot
+// be mapped, the empty string is returned.
+//
+func (m *Mapping) ToRelative(fpath string) string {
+ for _, e := range m.list {
+ if strings.HasPrefix(fpath, e.path) {
+ spath := filepath.ToSlash(fpath)
+ // /absolute/prefix/foo -> prefix/foo
+ return path.Join(e.prefix, spath[len(e.path):]) // Join will remove a trailing '/'
+ }
+ }
+ return "" // no match
+}
+
+// Iterate calls f for each path and RWValue in the mapping (in uspecified order)
+// until f returns false.
+//
+func (m *Mapping) Iterate(f func(path string, value *RWValue) bool) {
+ for _, e := range m.list {
+ if !f(e.path, e.value) {
+ return
+ }
+ }
+}