// 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):]) }