summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/github.com/awslabs/aws-sdk-go/aws/awsutil/path_value.go
blob: ce4c5e27ac94e8b61b9e97b4180f91627fc838ca (plain)
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
package awsutil

import (
	"reflect"
	"regexp"
	"strconv"
	"strings"
)

var indexRe = regexp.MustCompile(`(.+)\[(-?\d+)?\]$`)

func rValuesAtPath(v interface{}, path string, create bool) []reflect.Value {
	pathparts := strings.Split(path, "||")
	if len(pathparts) > 1 {
		for _, pathpart := range pathparts {
			vals := rValuesAtPath(v, pathpart, create)
			if vals != nil && len(vals) > 0 {
				return vals
			}
		}
		return nil
	}

	values := []reflect.Value{reflect.Indirect(reflect.ValueOf(v))}
	components := strings.Split(path, ".")
	for len(values) > 0 && len(components) > 0 {
		var index *int64
		var indexStar bool
		c := strings.TrimSpace(components[0])
		if c == "" { // no actual component, illegal syntax
			return nil
		} else if c != "*" && strings.ToLower(c[0:1]) == c[0:1] {
			// TODO normalize case for user
			return nil // don't support unexported fields
		}

		// parse this component
		if m := indexRe.FindStringSubmatch(c); m != nil {
			c = m[1]
			if m[2] == "" {
				index = nil
				indexStar = true
			} else {
				i, _ := strconv.ParseInt(m[2], 10, 32)
				index = &i
				indexStar = false
			}
		}

		nextvals := []reflect.Value{}
		for _, value := range values {
			// pull component name out of struct member
			if value.Kind() != reflect.Struct {
				continue
			}

			if c == "*" { // pull all members
				for i := 0; i < value.NumField(); i++ {
					if f := reflect.Indirect(value.Field(i)); f.IsValid() {
						nextvals = append(nextvals, f)
					}
				}
				continue
			}

			value = value.FieldByName(c)
			if create && value.Kind() == reflect.Ptr && value.IsNil() {
				value.Set(reflect.New(value.Type().Elem()))
				value = value.Elem()
			} else {
				value = reflect.Indirect(value)
			}

			if value.IsValid() {
				nextvals = append(nextvals, value)
			}
		}
		values = nextvals

		if indexStar || index != nil {
			nextvals = []reflect.Value{}
			for _, value := range values {
				value := reflect.Indirect(value)
				if value.Kind() != reflect.Slice {
					continue
				}

				if indexStar { // grab all indices
					for i := 0; i < value.Len(); i++ {
						idx := reflect.Indirect(value.Index(i))
						if idx.IsValid() {
							nextvals = append(nextvals, idx)
						}
					}
					continue
				}

				// pull out index
				i := int(*index)
				if i >= value.Len() { // check out of bounds
					if create {
						// TODO resize slice
					} else {
						continue
					}
				} else if i < 0 { // support negative indexing
					i = value.Len() + i
				}
				value = reflect.Indirect(value.Index(i))

				if value.IsValid() {
					nextvals = append(nextvals, value)
				}
			}
			values = nextvals
		}

		components = components[1:]
	}
	return values
}

// ValuesAtPath returns a list of objects at the lexical path inside of a structure
func ValuesAtPath(i interface{}, path string) []interface{} {
	if rvals := rValuesAtPath(i, path, false); rvals != nil {
		vals := make([]interface{}, len(rvals))
		for i, rval := range rvals {
			vals[i] = rval.Interface()
		}
		return vals
	}
	return nil
}

// SetValueAtPath sets an object at the lexical path inside of a structure
func SetValueAtPath(i interface{}, path string, v interface{}) {
	if rvals := rValuesAtPath(i, path, true); rvals != nil {
		for _, rval := range rvals {
			rval.Set(reflect.ValueOf(v))
		}
	}
}