| // Copyright 2018 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. | |
| package language | |
| import ( | |
| "sort" | |
| "strings" | |
| ) | |
| // A Builder allows constructing a Tag from individual components. | |
| // Its main user is Compose in the top-level language package. | |
| type Builder struct { | |
| Tag Tag | |
| private string // the x extension | |
| variants []string | |
| extensions []string | |
| } | |
| // Make returns a new Tag from the current settings. | |
| func (b *Builder) Make() Tag { | |
| t := b.Tag | |
| if len(b.extensions) > 0 || len(b.variants) > 0 { | |
| sort.Sort(sortVariants(b.variants)) | |
| sort.Strings(b.extensions) | |
| if b.private != "" { | |
| b.extensions = append(b.extensions, b.private) | |
| } | |
| n := maxCoreSize + tokenLen(b.variants...) + tokenLen(b.extensions...) | |
| buf := make([]byte, n) | |
| p := t.genCoreBytes(buf) | |
| t.pVariant = byte(p) | |
| p += appendTokens(buf[p:], b.variants...) | |
| t.pExt = uint16(p) | |
| p += appendTokens(buf[p:], b.extensions...) | |
| t.str = string(buf[:p]) | |
| // We may not always need to remake the string, but when or when not | |
| // to do so is rather tricky. | |
| scan := makeScanner(buf[:p]) | |
| t, _ = parse(&scan, "") | |
| return t | |
| } else if b.private != "" { | |
| t.str = b.private | |
| t.RemakeString() | |
| } | |
| return t | |
| } | |
| // SetTag copies all the settings from a given Tag. Any previously set values | |
| // are discarded. | |
| func (b *Builder) SetTag(t Tag) { | |
| b.Tag.LangID = t.LangID | |
| b.Tag.RegionID = t.RegionID | |
| b.Tag.ScriptID = t.ScriptID | |
| // TODO: optimize | |
| b.variants = b.variants[:0] | |
| if variants := t.Variants(); variants != "" { | |
| for _, vr := range strings.Split(variants[1:], "-") { | |
| b.variants = append(b.variants, vr) | |
| } | |
| } | |
| b.extensions, b.private = b.extensions[:0], "" | |
| for _, e := range t.Extensions() { | |
| b.AddExt(e) | |
| } | |
| } | |
| // AddExt adds extension e to the tag. e must be a valid extension as returned | |
| // by Tag.Extension. If the extension already exists, it will be discarded, | |
| // except for a -u extension, where non-existing key-type pairs will added. | |
| func (b *Builder) AddExt(e string) { | |
| if e[0] == 'x' { | |
| if b.private == "" { | |
| b.private = e | |
| } | |
| return | |
| } | |
| for i, s := range b.extensions { | |
| if s[0] == e[0] { | |
| if e[0] == 'u' { | |
| b.extensions[i] += e[1:] | |
| } | |
| return | |
| } | |
| } | |
| b.extensions = append(b.extensions, e) | |
| } | |
| // SetExt sets the extension e to the tag. e must be a valid extension as | |
| // returned by Tag.Extension. If the extension already exists, it will be | |
| // overwritten, except for a -u extension, where the individual key-type pairs | |
| // will be set. | |
| func (b *Builder) SetExt(e string) { | |
| if e[0] == 'x' { | |
| b.private = e | |
| return | |
| } | |
| for i, s := range b.extensions { | |
| if s[0] == e[0] { | |
| if e[0] == 'u' { | |
| b.extensions[i] = e + s[1:] | |
| } else { | |
| b.extensions[i] = e | |
| } | |
| return | |
| } | |
| } | |
| b.extensions = append(b.extensions, e) | |
| } | |
| // AddVariant adds any number of variants. | |
| func (b *Builder) AddVariant(v ...string) { | |
| for _, v := range v { | |
| if v != "" { | |
| b.variants = append(b.variants, v) | |
| } | |
| } | |
| } | |
| // ClearVariants removes any variants previously added, including those | |
| // copied from a Tag in SetTag. | |
| func (b *Builder) ClearVariants() { | |
| b.variants = b.variants[:0] | |
| } | |
| // ClearExtensions removes any extensions previously added, including those | |
| // copied from a Tag in SetTag. | |
| func (b *Builder) ClearExtensions() { | |
| b.private = "" | |
| b.extensions = b.extensions[:0] | |
| } | |
| func tokenLen(token ...string) (n int) { | |
| for _, t := range token { | |
| n += len(t) + 1 | |
| } | |
| return | |
| } | |
| func appendTokens(b []byte, token ...string) int { | |
| p := 0 | |
| for _, t := range token { | |
| b[p] = '-' | |
| copy(b[p+1:], t) | |
| p += 1 + len(t) | |
| } | |
| return p | |
| } | |
| type sortVariants []string | |
| func (s sortVariants) Len() int { | |
| return len(s) | |
| } | |
| func (s sortVariants) Swap(i, j int) { | |
| s[j], s[i] = s[i], s[j] | |
| } | |
| func (s sortVariants) Less(i, j int) bool { | |
| return variantIndex[s[i]] < variantIndex[s[j]] | |
| } |