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