summaryrefslogtreecommitdiffhomepage
path: root/vendor/golang.org/x/text/unicode/cldr/slice.go
blob: 388c983ff133a648ddc0590b07e9a0c8d285474c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2013 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.

package cldr

import (
	"fmt"
	"reflect"
	"sort"
)

// Slice provides utilities for modifying slices of elements.
// It can be wrapped around any slice of which the element type implements
// interface Elem.
type Slice struct {
	ptr reflect.Value
	typ reflect.Type
}

// Value returns the reflect.Value of the underlying slice.
func (s *Slice) Value() reflect.Value {
	return s.ptr.Elem()
}

// MakeSlice wraps a pointer to a slice of Elems.
// It replaces the array pointed to by the slice so that subsequent modifications
// do not alter the data in a CLDR type.
// It panics if an incorrect type is passed.
func MakeSlice(slicePtr interface{}) Slice {
	ptr := reflect.ValueOf(slicePtr)
	if ptr.Kind() != reflect.Ptr {
		panic(fmt.Sprintf("MakeSlice: argument must be pointer to slice, found %v", ptr.Type()))
	}
	sl := ptr.Elem()
	if sl.Kind() != reflect.Slice {
		panic(fmt.Sprintf("MakeSlice: argument must point to a slice, found %v", sl.Type()))
	}
	intf := reflect.TypeOf((*Elem)(nil)).Elem()
	if !sl.Type().Elem().Implements(intf) {
		panic(fmt.Sprintf("MakeSlice: element type of slice (%v) does not implement Elem", sl.Type().Elem()))
	}
	nsl := reflect.MakeSlice(sl.Type(), sl.Len(), sl.Len())
	reflect.Copy(nsl, sl)
	sl.Set(nsl)
	return Slice{
		ptr: ptr,
		typ: sl.Type().Elem().Elem(),
	}
}

func (s Slice) indexForAttr(a string) []int {
	for i := iter(reflect.Zero(s.typ)); !i.done(); i.next() {
		if n, _ := xmlName(i.field()); n == a {
			return i.index
		}
	}
	panic(fmt.Sprintf("MakeSlice: no attribute %q for type %v", a, s.typ))
}

// Filter filters s to only include elements for which fn returns true.
func (s Slice) Filter(fn func(e Elem) bool) {
	k := 0
	sl := s.Value()
	for i := 0; i < sl.Len(); i++ {
		vi := sl.Index(i)
		if fn(vi.Interface().(Elem)) {
			sl.Index(k).Set(vi)
			k++
		}
	}
	sl.Set(sl.Slice(0, k))
}

// Group finds elements in s for which fn returns the same value and groups
// them in a new Slice.
func (s Slice) Group(fn func(e Elem) string) []Slice {
	m := make(map[string][]reflect.Value)
	sl := s.Value()
	for i := 0; i < sl.Len(); i++ {
		vi := sl.Index(i)
		key := fn(vi.Interface().(Elem))
		m[key] = append(m[key], vi)
	}
	keys := []string{}
	for k, _ := range m {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	res := []Slice{}
	for _, k := range keys {
		nsl := reflect.New(sl.Type())
		nsl.Elem().Set(reflect.Append(nsl.Elem(), m[k]...))
		res = append(res, MakeSlice(nsl.Interface()))
	}
	return res
}

// SelectAnyOf filters s to contain only elements for which attr matches
// any of the values.
func (s Slice) SelectAnyOf(attr string, values ...string) {
	index := s.indexForAttr(attr)
	s.Filter(func(e Elem) bool {
		vf := reflect.ValueOf(e).Elem().FieldByIndex(index)
		return in(values, vf.String())
	})
}

// SelectOnePerGroup filters s to include at most one element e per group of
// elements matching Key(attr), where e has an attribute a that matches any
// the values in v.
// If more than one element in a group matches a value in v preference
// is given to the element that matches the first value in v.
func (s Slice) SelectOnePerGroup(a string, v []string) {
	index := s.indexForAttr(a)
	grouped := s.Group(func(e Elem) string { return Key(e, a) })
	sl := s.Value()
	sl.Set(sl.Slice(0, 0))
	for _, g := range grouped {
		e := reflect.Value{}
		found := len(v)
		gsl := g.Value()
		for i := 0; i < gsl.Len(); i++ {
			vi := gsl.Index(i).Elem().FieldByIndex(index)
			j := 0
			for ; j < len(v) && v[j] != vi.String(); j++ {
			}
			if j < found {
				found = j
				e = gsl.Index(i)
			}
		}
		if found < len(v) {
			sl.Set(reflect.Append(sl, e))
		}
	}
}

// SelectDraft drops all elements from the list with a draft level smaller than d
// and selects the highest draft level of the remaining.
// This method assumes that the input CLDR is canonicalized.
func (s Slice) SelectDraft(d Draft) {
	s.SelectOnePerGroup("draft", drafts[len(drafts)-2-int(d):])
}