coverage.go 5.0 KB

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