compose.go raw

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