| // Copyright 2015 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| // +build ignore |
| |
| // Generator for currency-related data. |
| |
| package main |
| |
| import ( |
| "bytes" |
| "flag" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "os" |
| "sort" |
| "strconv" |
| "strings" |
| |
| "golang.org/x/text/cldr" |
| "golang.org/x/text/internal/gen" |
| "golang.org/x/text/internal/tag" |
| "golang.org/x/text/language" |
| ) |
| |
| var ( |
| test = flag.Bool("test", false, |
| "test existing tables; can be used to compare web data with package data.") |
| outputFile = flag.String("output", "tables.go", "output file") |
| |
| draft = flag.String("draft", |
| "contributed", |
| `Minimal draft requirements (approved, contributed, provisional, unconfirmed).`) |
| ) |
| |
| func main() { |
| gen.Init() |
| |
| rewriteCommon() |
| |
| // Read the CLDR zip file. |
| r := gen.OpenCLDRCoreZip() |
| defer r.Close() |
| |
| d := &cldr.Decoder{} |
| d.SetDirFilter("supplemental") |
| data, err := d.DecodeZip(r) |
| if err != nil { |
| log.Fatalf("DecodeZip: %v", err) |
| } |
| |
| w := gen.NewCodeWriter() |
| defer w.WriteGoFile(*outputFile, "currency") |
| |
| fmt.Fprintln(w, `import "golang.org/x/text/internal/tag"`) |
| |
| gen.WriteCLDRVersion(w) |
| genCurrencies(w, data.Supplemental()) |
| } |
| |
| func rewriteCommon() { |
| // Generate common.go |
| src, err := ioutil.ReadFile("gen_common.go") |
| if err != nil { |
| log.Fatal(err) |
| } |
| const toDelete = "// +build ignore\n\npackage main\n\n" |
| i := bytes.Index(src, []byte(toDelete)) |
| if i < 0 { |
| log.Fatalf("could not find %q in gen_common.go", toDelete) |
| } |
| w := &bytes.Buffer{} |
| w.Write(src[i+len(toDelete):]) |
| gen.WriteGoFile("common.go", "currency", w.Bytes()) |
| } |
| |
| var constants = []string{ |
| // Undefined and testing. |
| "XXX", "XTS", |
| // G11 currencies https://en.wikipedia.org/wiki/G10_currencies. |
| "USD", "EUR", "JPY", "GBP", "CHF", "AUD", "NZD", "CAD", "SEK", "NOK", "DKK", |
| // Precious metals. |
| "XAG", "XAU", "XPT", "XPD", |
| |
| // Additional common currencies as defined by CLDR. |
| "BRL", "CNY", "INR", "RUB", "HKD", "IDR", "KRW", "MXN", "PLN", "SAR", |
| "THB", "TRY", "TWD", "ZAR", |
| } |
| |
| func genCurrencies(w *gen.CodeWriter, data *cldr.SupplementalData) { |
| // 3-letter ISO currency codes |
| // Start with dummy to let index start at 1. |
| currencies := []string{"\x00\x00\x00\x00"} |
| |
| // currency codes |
| for _, reg := range data.CurrencyData.Region { |
| for _, cur := range reg.Currency { |
| currencies = append(currencies, cur.Iso4217) |
| } |
| } |
| |
| sort.Strings(currencies) |
| // Unique the elements. |
| k := 0 |
| for i := 1; i < len(currencies); i++ { |
| if currencies[k] != currencies[i] { |
| currencies[k+1] = currencies[i] |
| k++ |
| } |
| } |
| currencies = currencies[:k+1] |
| |
| // Close with dummy for simpler and faster searching. |
| currencies = append(currencies, "\xff\xff\xff\xff") |
| |
| // Write currency values. |
| fmt.Fprintln(w, "const (") |
| for _, c := range constants { |
| index := sort.SearchStrings(currencies, c) |
| fmt.Fprintf(w, "\t%s = %d\n", strings.ToLower(c), index) |
| } |
| fmt.Fprint(w, ")") |
| |
| // Compute currency-related data that we merge into the table. |
| for _, info := range data.CurrencyData.Fractions[0].Info { |
| if info.Iso4217 == "DEFAULT" { |
| continue |
| } |
| standard := getRoundingIndex(info.Digits, info.Rounding) |
| cash := getRoundingIndex(info.CashDigits, info.CashRounding) |
| |
| index := sort.SearchStrings(currencies, info.Iso4217) |
| currencies[index] += mkCurrencyInfo(standard, cash) |
| } |
| |
| // Set default values for entries that weren't touched. |
| for i, c := range currencies { |
| if len(c) == 3 { |
| currencies[i] += mkCurrencyInfo(0, 0) |
| } |
| } |
| |
| currencyIndex := tag.Index(strings.Join(currencies, "")) |
| w.WriteComment(` |
| currency holds an alphabetically sorted list of canonical 3-letter currency |
| identifiers. Each identifier is followed by a byte of type currencyInfo, |
| defined in gen_common.go.`) |
| w.WriteConst("currency", currencyIndex) |
| |
| // Hack alert: gofmt indents a trailing comment after an indented string. |
| // Ensure that the next thing written is not a comment. |
| numCurrencies := (len(currencyIndex) / 4) - 2 |
| w.WriteConst("numCurrencies", numCurrencies) |
| |
| // Create a table that maps regions to currencies. |
| regionToCurrency := []toCurrency{} |
| |
| for _, reg := range data.CurrencyData.Region { |
| if len(reg.Iso3166) != 2 { |
| log.Fatalf("Unexpected group %q in region data", reg.Iso3166) |
| } |
| if len(reg.Currency) == 0 { |
| continue |
| } |
| cur := reg.Currency[0] |
| if cur.To != "" || cur.Tender == "false" { |
| continue |
| } |
| regionToCurrency = append(regionToCurrency, toCurrency{ |
| region: regionToCode(language.MustParseRegion(reg.Iso3166)), |
| code: uint16(currencyIndex.Index([]byte(cur.Iso4217))), |
| }) |
| } |
| sort.Sort(byRegion(regionToCurrency)) |
| |
| w.WriteType(toCurrency{}) |
| w.WriteVar("regionToCurrency", regionToCurrency) |
| } |
| |
| type toCurrency struct { |
| region uint16 |
| code uint16 |
| } |
| |
| type byRegion []toCurrency |
| |
| func (a byRegion) Len() int { return len(a) } |
| func (a byRegion) Swap(i, j int) { a[i], a[j] = a[j], a[i] } |
| func (a byRegion) Less(i, j int) bool { return a[i].region < a[j].region } |
| |
| func mkCurrencyInfo(standard, cash int) string { |
| return string([]byte{byte(cash<<cashShift | standard)}) |
| } |
| |
| func getRoundingIndex(digits, rounding string) int { |
| round := roundings[0] // default |
| |
| if digits != "" { |
| round.scale = parseUint8(digits) |
| } |
| if rounding != "" && rounding != "0" { // 0 means 1 here in CLDR |
| round.increment = parseUint8(rounding) |
| } |
| |
| // Will panic if the entry doesn't exist: |
| for i, r := range roundings { |
| if r == round { |
| return i |
| } |
| } |
| log.Fatalf("Rounding entry %#v does not exist.", round) |
| panic("unreachable") |
| } |
| |
| func parseUint8(str string) uint8 { |
| x, err := strconv.ParseUint(str, 10, 8) |
| if err != nil { |
| // Show line number of where this function was called. |
| log.New(os.Stderr, "", log.Lshortfile).Output(2, err.Error()) |
| os.Exit(1) |
| } |
| return uint8(x) |
| } |