compose.go (3881B)
1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package language 6 7 import ( 8 "sort" 9 "strings" 10 ) 11 12 // A Builder allows constructing a Tag from individual components. 13 // Its main user is Compose in the top-level language package. 14 type Builder struct { 15 Tag Tag 16 17 private string // the x extension 18 variants []string 19 extensions []string 20 } 21 22 // Make returns a new Tag from the current settings. 23 func (b *Builder) Make() Tag { 24 t := b.Tag 25 26 if len(b.extensions) > 0 || len(b.variants) > 0 { 27 sort.Sort(sortVariants(b.variants)) 28 sort.Strings(b.extensions) 29 30 if b.private != "" { 31 b.extensions = append(b.extensions, b.private) 32 } 33 n := maxCoreSize + tokenLen(b.variants...) + tokenLen(b.extensions...) 34 buf := make([]byte, n) 35 p := t.genCoreBytes(buf) 36 t.pVariant = byte(p) 37 p += appendTokens(buf[p:], b.variants...) 38 t.pExt = uint16(p) 39 p += appendTokens(buf[p:], b.extensions...) 40 t.str = string(buf[:p]) 41 // We may not always need to remake the string, but when or when not 42 // to do so is rather tricky. 43 scan := makeScanner(buf[:p]) 44 t, _ = parse(&scan, "") 45 return t 46 47 } else if b.private != "" { 48 t.str = b.private 49 t.RemakeString() 50 } 51 return t 52 } 53 54 // SetTag copies all the settings from a given Tag. Any previously set values 55 // are discarded. 56 func (b *Builder) SetTag(t Tag) { 57 b.Tag.LangID = t.LangID 58 b.Tag.RegionID = t.RegionID 59 b.Tag.ScriptID = t.ScriptID 60 // TODO: optimize 61 b.variants = b.variants[:0] 62 if variants := t.Variants(); variants != "" { 63 for _, vr := range strings.Split(variants[1:], "-") { 64 b.variants = append(b.variants, vr) 65 } 66 } 67 b.extensions, b.private = b.extensions[:0], "" 68 for _, e := range t.Extensions() { 69 b.AddExt(e) 70 } 71 } 72 73 // AddExt adds extension e to the tag. e must be a valid extension as returned 74 // by Tag.Extension. If the extension already exists, it will be discarded, 75 // except for a -u extension, where non-existing key-type pairs will added. 76 func (b *Builder) AddExt(e string) { 77 if e[0] == 'x' { 78 if b.private == "" { 79 b.private = e 80 } 81 return 82 } 83 for i, s := range b.extensions { 84 if s[0] == e[0] { 85 if e[0] == 'u' { 86 b.extensions[i] += e[1:] 87 } 88 return 89 } 90 } 91 b.extensions = append(b.extensions, e) 92 } 93 94 // SetExt sets the extension e to the tag. e must be a valid extension as 95 // returned by Tag.Extension. If the extension already exists, it will be 96 // overwritten, except for a -u extension, where the individual key-type pairs 97 // will be set. 98 func (b *Builder) SetExt(e string) { 99 if e[0] == 'x' { 100 b.private = e 101 return 102 } 103 for i, s := range b.extensions { 104 if s[0] == e[0] { 105 if e[0] == 'u' { 106 b.extensions[i] = e + s[1:] 107 } else { 108 b.extensions[i] = e 109 } 110 return 111 } 112 } 113 b.extensions = append(b.extensions, e) 114 } 115 116 // AddVariant adds any number of variants. 117 func (b *Builder) AddVariant(v ...string) { 118 for _, v := range v { 119 if v != "" { 120 b.variants = append(b.variants, v) 121 } 122 } 123 } 124 125 // ClearVariants removes any variants previously added, including those 126 // copied from a Tag in SetTag. 127 func (b *Builder) ClearVariants() { 128 b.variants = b.variants[:0] 129 } 130 131 // ClearExtensions removes any extensions previously added, including those 132 // copied from a Tag in SetTag. 133 func (b *Builder) ClearExtensions() { 134 b.private = "" 135 b.extensions = b.extensions[:0] 136 } 137 138 func tokenLen(token ...string) (n int) { 139 for _, t := range token { 140 n += len(t) + 1 141 } 142 return 143 } 144 145 func appendTokens(b []byte, token ...string) int { 146 p := 0 147 for _, t := range token { 148 b[p] = '-' 149 copy(b[p+1:], t) 150 p += 1 + len(t) 151 } 152 return p 153 } 154 155 type sortVariants []string 156 157 func (s sortVariants) Len() int { 158 return len(s) 159 } 160 161 func (s sortVariants) Swap(i, j int) { 162 s[j], s[i] = s[i], s[j] 163 } 164 165 func (s sortVariants) Less(i, j int) bool { 166 return variantIndex[s[i]] < variantIndex[s[j]] 167 }