mirror

Mirror free and open-source projects you like with minimal effort
git clone git://git.server.ky/slackcoder/mirror
Log | Files | Refs | README

type_fields.go (6475B)


      1 package toml
      2 
      3 // Struct field handling is adapted from code in encoding/json:
      4 //
      5 // Copyright 2010 The Go Authors.  All rights reserved.
      6 // Use of this source code is governed by a BSD-style
      7 // license that can be found in the Go distribution.
      8 
      9 import (
     10 	"reflect"
     11 	"sort"
     12 	"sync"
     13 )
     14 
     15 // A field represents a single field found in a struct.
     16 type field struct {
     17 	name  string       // the name of the field (`toml` tag included)
     18 	tag   bool         // whether field has a `toml` tag
     19 	index []int        // represents the depth of an anonymous field
     20 	typ   reflect.Type // the type of the field
     21 }
     22 
     23 // byName sorts field by name, breaking ties with depth,
     24 // then breaking ties with "name came from toml tag", then
     25 // breaking ties with index sequence.
     26 type byName []field
     27 
     28 func (x byName) Len() int      { return len(x) }
     29 func (x byName) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
     30 func (x byName) Less(i, j int) bool {
     31 	if x[i].name != x[j].name {
     32 		return x[i].name < x[j].name
     33 	}
     34 	if len(x[i].index) != len(x[j].index) {
     35 		return len(x[i].index) < len(x[j].index)
     36 	}
     37 	if x[i].tag != x[j].tag {
     38 		return x[i].tag
     39 	}
     40 	return byIndex(x).Less(i, j)
     41 }
     42 
     43 // byIndex sorts field by index sequence.
     44 type byIndex []field
     45 
     46 func (x byIndex) Len() int      { return len(x) }
     47 func (x byIndex) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
     48 func (x byIndex) Less(i, j int) bool {
     49 	for k, xik := range x[i].index {
     50 		if k >= len(x[j].index) {
     51 			return false
     52 		}
     53 		if xik != x[j].index[k] {
     54 			return xik < x[j].index[k]
     55 		}
     56 	}
     57 	return len(x[i].index) < len(x[j].index)
     58 }
     59 
     60 // typeFields returns a list of fields that TOML should recognize for the given
     61 // type. The algorithm is breadth-first search over the set of structs to
     62 // include - the top struct and then any reachable anonymous structs.
     63 func typeFields(t reflect.Type) []field {
     64 	// Anonymous fields to explore at the current level and the next.
     65 	current := []field{}
     66 	next := []field{{typ: t}}
     67 
     68 	// Count of queued names for current level and the next.
     69 	var count map[reflect.Type]int
     70 	var nextCount map[reflect.Type]int
     71 
     72 	// Types already visited at an earlier level.
     73 	visited := map[reflect.Type]bool{}
     74 
     75 	// Fields found.
     76 	var fields []field
     77 
     78 	for len(next) > 0 {
     79 		current, next = next, current[:0]
     80 		count, nextCount = nextCount, map[reflect.Type]int{}
     81 
     82 		for _, f := range current {
     83 			if visited[f.typ] {
     84 				continue
     85 			}
     86 			visited[f.typ] = true
     87 
     88 			// Scan f.typ for fields to include.
     89 			for i := 0; i < f.typ.NumField(); i++ {
     90 				sf := f.typ.Field(i)
     91 				if sf.PkgPath != "" && !sf.Anonymous { // unexported
     92 					continue
     93 				}
     94 				opts := getOptions(sf.Tag)
     95 				if opts.skip {
     96 					continue
     97 				}
     98 				index := make([]int, len(f.index)+1)
     99 				copy(index, f.index)
    100 				index[len(f.index)] = i
    101 
    102 				ft := sf.Type
    103 				if ft.Name() == "" && ft.Kind() == reflect.Ptr {
    104 					// Follow pointer.
    105 					ft = ft.Elem()
    106 				}
    107 
    108 				// Record found field and index sequence.
    109 				if opts.name != "" || !sf.Anonymous || ft.Kind() != reflect.Struct {
    110 					tagged := opts.name != ""
    111 					name := opts.name
    112 					if name == "" {
    113 						name = sf.Name
    114 					}
    115 					fields = append(fields, field{name, tagged, index, ft})
    116 					if count[f.typ] > 1 {
    117 						// If there were multiple instances, add a second,
    118 						// so that the annihilation code will see a duplicate.
    119 						// It only cares about the distinction between 1 or 2,
    120 						// so don't bother generating any more copies.
    121 						fields = append(fields, fields[len(fields)-1])
    122 					}
    123 					continue
    124 				}
    125 
    126 				// Record new anonymous struct to explore in next round.
    127 				nextCount[ft]++
    128 				if nextCount[ft] == 1 {
    129 					f := field{name: ft.Name(), index: index, typ: ft}
    130 					next = append(next, f)
    131 				}
    132 			}
    133 		}
    134 	}
    135 
    136 	sort.Sort(byName(fields))
    137 
    138 	// Delete all fields that are hidden by the Go rules for embedded fields,
    139 	// except that fields with TOML tags are promoted.
    140 
    141 	// The fields are sorted in primary order of name, secondary order
    142 	// of field index length. Loop over names; for each name, delete
    143 	// hidden fields by choosing the one dominant field that survives.
    144 	out := fields[:0]
    145 	for advance, i := 0, 0; i < len(fields); i += advance {
    146 		// One iteration per name.
    147 		// Find the sequence of fields with the name of this first field.
    148 		fi := fields[i]
    149 		name := fi.name
    150 		for advance = 1; i+advance < len(fields); advance++ {
    151 			fj := fields[i+advance]
    152 			if fj.name != name {
    153 				break
    154 			}
    155 		}
    156 		if advance == 1 { // Only one field with this name
    157 			out = append(out, fi)
    158 			continue
    159 		}
    160 		dominant, ok := dominantField(fields[i : i+advance])
    161 		if ok {
    162 			out = append(out, dominant)
    163 		}
    164 	}
    165 
    166 	fields = out
    167 	sort.Sort(byIndex(fields))
    168 
    169 	return fields
    170 }
    171 
    172 // dominantField looks through the fields, all of which are known to
    173 // have the same name, to find the single field that dominates the
    174 // others using Go's embedding rules, modified by the presence of
    175 // TOML tags. If there are multiple top-level fields, the boolean
    176 // will be false: This condition is an error in Go and we skip all
    177 // the fields.
    178 func dominantField(fields []field) (field, bool) {
    179 	// The fields are sorted in increasing index-length order. The winner
    180 	// must therefore be one with the shortest index length. Drop all
    181 	// longer entries, which is easy: just truncate the slice.
    182 	length := len(fields[0].index)
    183 	tagged := -1 // Index of first tagged field.
    184 	for i, f := range fields {
    185 		if len(f.index) > length {
    186 			fields = fields[:i]
    187 			break
    188 		}
    189 		if f.tag {
    190 			if tagged >= 0 {
    191 				// Multiple tagged fields at the same level: conflict.
    192 				// Return no field.
    193 				return field{}, false
    194 			}
    195 			tagged = i
    196 		}
    197 	}
    198 	if tagged >= 0 {
    199 		return fields[tagged], true
    200 	}
    201 	// All remaining fields have the same length. If there's more than one,
    202 	// we have a conflict (two fields named "X" at the same level) and we
    203 	// return no field.
    204 	if len(fields) > 1 {
    205 		return field{}, false
    206 	}
    207 	return fields[0], true
    208 }
    209 
    210 var fieldCache struct {
    211 	sync.RWMutex
    212 	m map[reflect.Type][]field
    213 }
    214 
    215 // cachedTypeFields is like typeFields but uses a cache to avoid repeated work.
    216 func cachedTypeFields(t reflect.Type) []field {
    217 	fieldCache.RLock()
    218 	f := fieldCache.m[t]
    219 	fieldCache.RUnlock()
    220 	if f != nil {
    221 		return f
    222 	}
    223 
    224 	// Compute fields without lock.
    225 	// Might duplicate effort but won't hold other computations back.
    226 	f = typeFields(t)
    227 	if f == nil {
    228 		f = []field{}
    229 	}
    230 
    231 	fieldCache.Lock()
    232 	if fieldCache.m == nil {
    233 		fieldCache.m = map[reflect.Type][]field{}
    234 	}
    235 	fieldCache.m[t] = f
    236 	fieldCache.Unlock()
    237 	return f
    238 }