coverage.go

  1// Copyright 2014 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	"fmt"
  9	"sort"
 10
 11	"golang.org/x/text/internal/language"
 12)
 13
 14// The Coverage interface is used to define the level of coverage of an
 15// internationalization service. Note that not all types are supported by all
 16// services. As lists may be generated on the fly, it is recommended that users
 17// of a Coverage cache the results.
 18type Coverage interface {
 19	// Tags returns the list of supported tags.
 20	Tags() []Tag
 21
 22	// BaseLanguages returns the list of supported base languages.
 23	BaseLanguages() []Base
 24
 25	// Scripts returns the list of supported scripts.
 26	Scripts() []Script
 27
 28	// Regions returns the list of supported regions.
 29	Regions() []Region
 30}
 31
 32var (
 33	// Supported defines a Coverage that lists all supported subtags. Tags
 34	// always returns nil.
 35	Supported Coverage = allSubtags{}
 36)
 37
 38// TODO:
 39// - Support Variants, numbering systems.
 40// - CLDR coverage levels.
 41// - Set of common tags defined in this package.
 42
 43type allSubtags struct{}
 44
 45// Regions returns the list of supported regions. As all regions are in a
 46// consecutive range, it simply returns a slice of numbers in increasing order.
 47// The "undefined" region is not returned.
 48func (s allSubtags) Regions() []Region {
 49	reg := make([]Region, language.NumRegions)
 50	for i := range reg {
 51		reg[i] = Region{language.Region(i + 1)}
 52	}
 53	return reg
 54}
 55
 56// Scripts returns the list of supported scripts. As all scripts are in a
 57// consecutive range, it simply returns a slice of numbers in increasing order.
 58// The "undefined" script is not returned.
 59func (s allSubtags) Scripts() []Script {
 60	scr := make([]Script, language.NumScripts)
 61	for i := range scr {
 62		scr[i] = Script{language.Script(i + 1)}
 63	}
 64	return scr
 65}
 66
 67// BaseLanguages returns the list of all supported base languages. It generates
 68// the list by traversing the internal structures.
 69func (s allSubtags) BaseLanguages() []Base {
 70	bs := language.BaseLanguages()
 71	base := make([]Base, len(bs))
 72	for i, b := range bs {
 73		base[i] = Base{b}
 74	}
 75	return base
 76}
 77
 78// Tags always returns nil.
 79func (s allSubtags) Tags() []Tag {
 80	return nil
 81}
 82
 83// coverage is used by NewCoverage which is used as a convenient way for
 84// creating Coverage implementations for partially defined data. Very often a
 85// package will only need to define a subset of slices. coverage provides a
 86// convenient way to do this. Moreover, packages using NewCoverage, instead of
 87// their own implementation, will not break if later new slice types are added.
 88type coverage struct {
 89	tags    func() []Tag
 90	bases   func() []Base
 91	scripts func() []Script
 92	regions func() []Region
 93}
 94
 95func (s *coverage) Tags() []Tag {
 96	if s.tags == nil {
 97		return nil
 98	}
 99	return s.tags()
100}
101
102// bases implements sort.Interface and is used to sort base languages.
103type bases []Base
104
105func (b bases) Len() int {
106	return len(b)
107}
108
109func (b bases) Swap(i, j int) {
110	b[i], b[j] = b[j], b[i]
111}
112
113func (b bases) Less(i, j int) bool {
114	return b[i].langID < b[j].langID
115}
116
117// BaseLanguages returns the result from calling s.bases if it is specified or
118// otherwise derives the set of supported base languages from tags.
119func (s *coverage) BaseLanguages() []Base {
120	if s.bases == nil {
121		tags := s.Tags()
122		if len(tags) == 0 {
123			return nil
124		}
125		a := make([]Base, len(tags))
126		for i, t := range tags {
127			a[i] = Base{language.Language(t.lang())}
128		}
129		sort.Sort(bases(a))
130		k := 0
131		for i := 1; i < len(a); i++ {
132			if a[k] != a[i] {
133				k++
134				a[k] = a[i]
135			}
136		}
137		return a[:k+1]
138	}
139	return s.bases()
140}
141
142func (s *coverage) Scripts() []Script {
143	if s.scripts == nil {
144		return nil
145	}
146	return s.scripts()
147}
148
149func (s *coverage) Regions() []Region {
150	if s.regions == nil {
151		return nil
152	}
153	return s.regions()
154}
155
156// NewCoverage returns a Coverage for the given lists. It is typically used by
157// packages providing internationalization services to define their level of
158// coverage. A list may be of type []T or func() []T, where T is either Tag,
159// Base, Script or Region. The returned Coverage derives the value for Bases
160// from Tags if no func or slice for []Base is specified. For other unspecified
161// types the returned Coverage will return nil for the respective methods.
162func NewCoverage(list ...interface{}) Coverage {
163	s := &coverage{}
164	for _, x := range list {
165		switch v := x.(type) {
166		case func() []Base:
167			s.bases = v
168		case func() []Script:
169			s.scripts = v
170		case func() []Region:
171			s.regions = v
172		case func() []Tag:
173			s.tags = v
174		case []Base:
175			s.bases = func() []Base { return v }
176		case []Script:
177			s.scripts = func() []Script { return v }
178		case []Region:
179			s.regions = func() []Region { return v }
180		case []Tag:
181			s.tags = func() []Tag { return v }
182		default:
183			panic(fmt.Sprintf("language: unsupported set type %T", v))
184		}
185	}
186	return s
187}