compose.go 3.8 KB
// 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]]
}