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 }