1// Copyright 2013 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 cldr
6
7import (
8	"fmt"
9	"reflect"
10	"sort"
11)
12
13// Slice provides utilities for modifying slices of elements.
14// It can be wrapped around any slice of which the element type implements
15// interface Elem.
16type Slice struct {
17	ptr reflect.Value
18	typ reflect.Type
19}
20
21// Value returns the reflect.Value of the underlying slice.
22func (s *Slice) Value() reflect.Value {
23	return s.ptr.Elem()
24}
25
26// MakeSlice wraps a pointer to a slice of Elems.
27// It replaces the array pointed to by the slice so that subsequent modifications
28// do not alter the data in a CLDR type.
29// It panics if an incorrect type is passed.
30func MakeSlice(slicePtr interface{}) Slice {
31	ptr := reflect.ValueOf(slicePtr)
32	if ptr.Kind() != reflect.Ptr {
33		panic(fmt.Sprintf("MakeSlice: argument must be pointer to slice, found %v", ptr.Type()))
34	}
35	sl := ptr.Elem()
36	if sl.Kind() != reflect.Slice {
37		panic(fmt.Sprintf("MakeSlice: argument must point to a slice, found %v", sl.Type()))
38	}
39	intf := reflect.TypeOf((*Elem)(nil)).Elem()
40	if !sl.Type().Elem().Implements(intf) {
41		panic(fmt.Sprintf("MakeSlice: element type of slice (%v) does not implement Elem", sl.Type().Elem()))
42	}
43	nsl := reflect.MakeSlice(sl.Type(), sl.Len(), sl.Len())
44	reflect.Copy(nsl, sl)
45	sl.Set(nsl)
46	return Slice{
47		ptr: ptr,
48		typ: sl.Type().Elem().Elem(),
49	}
50}
51
52func (s Slice) indexForAttr(a string) []int {
53	for i := iter(reflect.Zero(s.typ)); !i.done(); i.next() {
54		if n, _ := xmlName(i.field()); n == a {
55			return i.index
56		}
57	}
58	panic(fmt.Sprintf("MakeSlice: no attribute %q for type %v", a, s.typ))
59}
60
61// Filter filters s to only include elements for which fn returns true.
62func (s Slice) Filter(fn func(e Elem) bool) {
63	k := 0
64	sl := s.Value()
65	for i := 0; i < sl.Len(); i++ {
66		vi := sl.Index(i)
67		if fn(vi.Interface().(Elem)) {
68			sl.Index(k).Set(vi)
69			k++
70		}
71	}
72	sl.Set(sl.Slice(0, k))
73}
74
75// Group finds elements in s for which fn returns the same value and groups
76// them in a new Slice.
77func (s Slice) Group(fn func(e Elem) string) []Slice {
78	m := make(map[string][]reflect.Value)
79	sl := s.Value()
80	for i := 0; i < sl.Len(); i++ {
81		vi := sl.Index(i)
82		key := fn(vi.Interface().(Elem))
83		m[key] = append(m[key], vi)
84	}
85	keys := []string{}
86	for k, _ := range m {
87		keys = append(keys, k)
88	}
89	sort.Strings(keys)
90	res := []Slice{}
91	for _, k := range keys {
92		nsl := reflect.New(sl.Type())
93		nsl.Elem().Set(reflect.Append(nsl.Elem(), m[k]...))
94		res = append(res, MakeSlice(nsl.Interface()))
95	}
96	return res
97}
98
99// SelectAnyOf filters s to contain only elements for which attr matches
100// any of the values.
101func (s Slice) SelectAnyOf(attr string, values ...string) {
102	index := s.indexForAttr(attr)
103	s.Filter(func(e Elem) bool {
104		vf := reflect.ValueOf(e).Elem().FieldByIndex(index)
105		return in(values, vf.String())
106	})
107}
108
109// SelectOnePerGroup filters s to include at most one element e per group of
110// elements matching Key(attr), where e has an attribute a that matches any
111// the values in v.
112// If more than one element in a group matches a value in v preference
113// is given to the element that matches the first value in v.
114func (s Slice) SelectOnePerGroup(a string, v []string) {
115	index := s.indexForAttr(a)
116	grouped := s.Group(func(e Elem) string { return Key(e, a) })
117	sl := s.Value()
118	sl.Set(sl.Slice(0, 0))
119	for _, g := range grouped {
120		e := reflect.Value{}
121		found := len(v)
122		gsl := g.Value()
123		for i := 0; i < gsl.Len(); i++ {
124			vi := gsl.Index(i).Elem().FieldByIndex(index)
125			j := 0
126			for ; j < len(v) && v[j] != vi.String(); j++ {
127			}
128			if j < found {
129				found = j
130				e = gsl.Index(i)
131			}
132		}
133		if found < len(v) {
134			sl.Set(reflect.Append(sl, e))
135		}
136	}
137}
138
139// SelectDraft drops all elements from the list with a draft level smaller than d
140// and selects the highest draft level of the remaining.
141// This method assumes that the input CLDR is canonicalized.
142func (s Slice) SelectDraft(d Draft) {
143	s.SelectOnePerGroup("draft", drafts[len(drafts)-2-int(d):])
144}
145