coverage.go raw

   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  
   5  package language
   6  
   7  import (
   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.
  18  type 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  
  32  var (
  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  
  43  type 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.
  48  func (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.
  59  func (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.
  69  func (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.
  79  func (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.
  88  type coverage struct {
  89  	tags    func() []Tag
  90  	bases   func() []Base
  91  	scripts func() []Script
  92  	regions func() []Region
  93  }
  94  
  95  func (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.
 103  type bases []Base
 104  
 105  func (b bases) Len() int {
 106  	return len(b)
 107  }
 108  
 109  func (b bases) Swap(i, j int) {
 110  	b[i], b[j] = b[j], b[i]
 111  }
 112  
 113  func (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.
 119  func (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  
 142  func (s *coverage) Scripts() []Script {
 143  	if s.scripts == nil {
 144  		return nil
 145  	}
 146  	return s.scripts()
 147  }
 148  
 149  func (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.
 162  func 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  }
 188