gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

compose.go (3881B)


      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 }