You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
3.8 KiB
167 lines
3.8 KiB
// 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]] |
|
}
|
|
|