taxonomy.go (4848B)
1 // Copyright 2019 The Hugo Authors. 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 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13
14 package hugolib
15
16 import (
17 "fmt"
18 "sort"
19
20 "github.com/gohugoio/hugo/compare"
21 "github.com/gohugoio/hugo/langs"
22
23 "github.com/gohugoio/hugo/resources/page"
24 )
25
26 // The TaxonomyList is a list of all taxonomies and their values
27 // e.g. List['tags'] => TagTaxonomy (from above)
28 type TaxonomyList map[string]Taxonomy
29
30 func (tl TaxonomyList) String() string {
31 return fmt.Sprintf("TaxonomyList(%d)", len(tl))
32 }
33
34 // A Taxonomy is a map of keywords to a list of pages.
35 // For example
36 // TagTaxonomy['technology'] = page.WeightedPages
37 // TagTaxonomy['go'] = page.WeightedPages
38 type Taxonomy map[string]page.WeightedPages
39
40 // OrderedTaxonomy is another representation of an Taxonomy using an array rather than a map.
41 // Important because you can't order a map.
42 type OrderedTaxonomy []OrderedTaxonomyEntry
43
44 // getOneOPage returns one page in the taxonomy,
45 // nil if there is none.
46 func (t OrderedTaxonomy) getOneOPage() page.Page {
47 if len(t) == 0 {
48 return nil
49 }
50 return t[0].Pages()[0]
51 }
52
53 // OrderedTaxonomyEntry is similar to an element of a Taxonomy, but with the key embedded (as name)
54 // e.g: {Name: Technology, page.WeightedPages: TaxonomyPages}
55 type OrderedTaxonomyEntry struct {
56 Name string
57 page.WeightedPages
58 }
59
60 // Get the weighted pages for the given key.
61 func (i Taxonomy) Get(key string) page.WeightedPages {
62 return i[key]
63 }
64
65 // Count the weighted pages for the given key.
66 func (i Taxonomy) Count(key string) int { return len(i[key]) }
67
68 func (i Taxonomy) add(key string, w page.WeightedPage) {
69 i[key] = append(i[key], w)
70 }
71
72 // TaxonomyArray returns an ordered taxonomy with a non defined order.
73 func (i Taxonomy) TaxonomyArray() OrderedTaxonomy {
74 ies := make([]OrderedTaxonomyEntry, len(i))
75 count := 0
76 for k, v := range i {
77 ies[count] = OrderedTaxonomyEntry{Name: k, WeightedPages: v}
78 count++
79 }
80 return ies
81 }
82
83 // Alphabetical returns an ordered taxonomy sorted by key name.
84 func (i Taxonomy) Alphabetical() OrderedTaxonomy {
85 ia := i.TaxonomyArray()
86 p := ia.getOneOPage()
87 if p == nil {
88 return ia
89 }
90 currentSite := p.Site().Current()
91 coll := langs.GetCollator(currentSite.Language())
92 coll.Lock()
93 defer coll.Unlock()
94 name := func(i1, i2 *OrderedTaxonomyEntry) bool {
95 return coll.CompareStrings(i1.Name, i2.Name) < 0
96 }
97 oiBy(name).Sort(ia)
98 return ia
99 }
100
101 // ByCount returns an ordered taxonomy sorted by # of pages per key.
102 // If taxonomies have the same # of pages, sort them alphabetical
103 func (i Taxonomy) ByCount() OrderedTaxonomy {
104 count := func(i1, i2 *OrderedTaxonomyEntry) bool {
105 li1 := len(i1.WeightedPages)
106 li2 := len(i2.WeightedPages)
107
108 if li1 == li2 {
109 return compare.LessStrings(i1.Name, i2.Name)
110 }
111 return li1 > li2
112 }
113
114 ia := i.TaxonomyArray()
115 oiBy(count).Sort(ia)
116 return ia
117 }
118
119 // Pages returns the Pages for this taxonomy.
120 func (ie OrderedTaxonomyEntry) Pages() page.Pages {
121 return ie.WeightedPages.Pages()
122 }
123
124 // Count returns the count the pages in this taxonomy.
125 func (ie OrderedTaxonomyEntry) Count() int {
126 return len(ie.WeightedPages)
127 }
128
129 // Term returns the name given to this taxonomy.
130 func (ie OrderedTaxonomyEntry) Term() string {
131 return ie.Name
132 }
133
134 // Reverse reverses the order of the entries in this taxonomy.
135 func (t OrderedTaxonomy) Reverse() OrderedTaxonomy {
136 for i, j := 0, len(t)-1; i < j; i, j = i+1, j-1 {
137 t[i], t[j] = t[j], t[i]
138 }
139
140 return t
141 }
142
143 // A type to implement the sort interface for TaxonomyEntries.
144 type orderedTaxonomySorter struct {
145 taxonomy OrderedTaxonomy
146 by oiBy
147 }
148
149 // Closure used in the Sort.Less method.
150 type oiBy func(i1, i2 *OrderedTaxonomyEntry) bool
151
152 func (by oiBy) Sort(taxonomy OrderedTaxonomy) {
153 ps := &orderedTaxonomySorter{
154 taxonomy: taxonomy,
155 by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
156 }
157 sort.Stable(ps)
158 }
159
160 // Len is part of sort.Interface.
161 func (s *orderedTaxonomySorter) Len() int {
162 return len(s.taxonomy)
163 }
164
165 // Swap is part of sort.Interface.
166 func (s *orderedTaxonomySorter) Swap(i, j int) {
167 s.taxonomy[i], s.taxonomy[j] = s.taxonomy[j], s.taxonomy[i]
168 }
169
170 // Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
171 func (s *orderedTaxonomySorter) Less(i, j int) bool {
172 return s.by(&s.taxonomy[i], &s.taxonomy[j])
173 }