compose.go

  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
  5package language
  6
  7import (
  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.
 14type 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.
 23func (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.
 56func (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.
 76func (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.
 98func (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.
117func (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.
127func (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.
133func (b *Builder) ClearExtensions() {
134	b.private = ""
135	b.extensions = b.extensions[:0]
136}
137
138func tokenLen(token ...string) (n int) {
139	for _, t := range token {
140		n += len(t) + 1
141	}
142	return
143}
144
145func 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
155type sortVariants []string
156
157func (s sortVariants) Len() int {
158	return len(s)
159}
160
161func (s sortVariants) Swap(i, j int) {
162	s[j], s[i] = s[i], s[j]
163}
164
165func (s sortVariants) Less(i, j int) bool {
166	return variantIndex[s[i]] < variantIndex[s[j]]
167}