| // 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]] |
| } |