xref: /aosp_15_r20/build/blueprint/proptools/extend.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Worker// Copyright 2015 Google Inc. All rights reserved.
2*1fa6dee9SAndroid Build Coastguard Worker//
3*1fa6dee9SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*1fa6dee9SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*1fa6dee9SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*1fa6dee9SAndroid Build Coastguard Worker//
7*1fa6dee9SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*1fa6dee9SAndroid Build Coastguard Worker//
9*1fa6dee9SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*1fa6dee9SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*1fa6dee9SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*1fa6dee9SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*1fa6dee9SAndroid Build Coastguard Worker// limitations under the License.
14*1fa6dee9SAndroid Build Coastguard Worker
15*1fa6dee9SAndroid Build Coastguard Workerpackage proptools
16*1fa6dee9SAndroid Build Coastguard Worker
17*1fa6dee9SAndroid Build Coastguard Workerimport (
18*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
19*1fa6dee9SAndroid Build Coastguard Worker	"reflect"
20*1fa6dee9SAndroid Build Coastguard Worker	"slices"
21*1fa6dee9SAndroid Build Coastguard Worker	"strings"
22*1fa6dee9SAndroid Build Coastguard Worker)
23*1fa6dee9SAndroid Build Coastguard Worker
24*1fa6dee9SAndroid Build Coastguard Worker// AppendProperties appends the values of properties in the property struct src to the property
25*1fa6dee9SAndroid Build Coastguard Worker// struct dst. dst and src must be the same type, and both must be pointers to structs. Properties
26*1fa6dee9SAndroid Build Coastguard Worker// tagged `blueprint:"mutated"` are skipped.
27*1fa6dee9SAndroid Build Coastguard Worker//
28*1fa6dee9SAndroid Build Coastguard Worker// The filter function can prevent individual properties from being appended by returning false, or
29*1fa6dee9SAndroid Build Coastguard Worker// abort AppendProperties with an error by returning an error.  Passing nil for filter will append
30*1fa6dee9SAndroid Build Coastguard Worker// all properties.
31*1fa6dee9SAndroid Build Coastguard Worker//
32*1fa6dee9SAndroid Build Coastguard Worker// An error returned by AppendProperties that applies to a specific property will be an
33*1fa6dee9SAndroid Build Coastguard Worker// *ExtendPropertyError, and can have the property name and error extracted from it.
34*1fa6dee9SAndroid Build Coastguard Worker//
35*1fa6dee9SAndroid Build Coastguard Worker// The append operation is defined as appending strings and slices of strings normally, OR-ing bool
36*1fa6dee9SAndroid Build Coastguard Worker// values, replacing non-nil pointers to booleans or strings, and recursing into
37*1fa6dee9SAndroid Build Coastguard Worker// embedded structs, pointers to structs, and interfaces containing
38*1fa6dee9SAndroid Build Coastguard Worker// pointers to structs.  Appending the zero value of a property will always be a no-op.
39*1fa6dee9SAndroid Build Coastguard Workerfunc AppendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error {
40*1fa6dee9SAndroid Build Coastguard Worker	return extendProperties(dst, src, filter, OrderAppend)
41*1fa6dee9SAndroid Build Coastguard Worker}
42*1fa6dee9SAndroid Build Coastguard Worker
43*1fa6dee9SAndroid Build Coastguard Worker// PrependProperties prepends the values of properties in the property struct src to the property
44*1fa6dee9SAndroid Build Coastguard Worker// struct dst. dst and src must be the same type, and both must be pointers to structs. Properties
45*1fa6dee9SAndroid Build Coastguard Worker// tagged `blueprint:"mutated"` are skipped.
46*1fa6dee9SAndroid Build Coastguard Worker//
47*1fa6dee9SAndroid Build Coastguard Worker// The filter function can prevent individual properties from being prepended by returning false, or
48*1fa6dee9SAndroid Build Coastguard Worker// abort PrependProperties with an error by returning an error.  Passing nil for filter will prepend
49*1fa6dee9SAndroid Build Coastguard Worker// all properties.
50*1fa6dee9SAndroid Build Coastguard Worker//
51*1fa6dee9SAndroid Build Coastguard Worker// An error returned by PrependProperties that applies to a specific property will be an
52*1fa6dee9SAndroid Build Coastguard Worker// *ExtendPropertyError, and can have the property name and error extracted from it.
53*1fa6dee9SAndroid Build Coastguard Worker//
54*1fa6dee9SAndroid Build Coastguard Worker// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing
55*1fa6dee9SAndroid Build Coastguard Worker// bool values, replacing non-nil pointers to booleans or strings, and recursing into
56*1fa6dee9SAndroid Build Coastguard Worker// embedded structs, pointers to structs, and interfaces containing
57*1fa6dee9SAndroid Build Coastguard Worker// pointers to structs.  Prepending the zero value of a property will always be a no-op.
58*1fa6dee9SAndroid Build Coastguard Workerfunc PrependProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc) error {
59*1fa6dee9SAndroid Build Coastguard Worker	return extendProperties(dst, src, filter, OrderPrepend)
60*1fa6dee9SAndroid Build Coastguard Worker}
61*1fa6dee9SAndroid Build Coastguard Worker
62*1fa6dee9SAndroid Build Coastguard Worker// AppendMatchingProperties appends the values of properties in the property struct src to the
63*1fa6dee9SAndroid Build Coastguard Worker// property structs in dst.  dst and src do not have to be the same type, but every property in src
64*1fa6dee9SAndroid Build Coastguard Worker// must be found in at least one property in dst.  dst must be a slice of pointers to structs, and
65*1fa6dee9SAndroid Build Coastguard Worker// src must be a pointer to a struct.  Properties tagged `blueprint:"mutated"` are skipped.
66*1fa6dee9SAndroid Build Coastguard Worker//
67*1fa6dee9SAndroid Build Coastguard Worker// The filter function can prevent individual properties from being appended by returning false, or
68*1fa6dee9SAndroid Build Coastguard Worker// abort AppendProperties with an error by returning an error.  Passing nil for filter will append
69*1fa6dee9SAndroid Build Coastguard Worker// all properties.
70*1fa6dee9SAndroid Build Coastguard Worker//
71*1fa6dee9SAndroid Build Coastguard Worker// An error returned by AppendMatchingProperties that applies to a specific property will be an
72*1fa6dee9SAndroid Build Coastguard Worker// *ExtendPropertyError, and can have the property name and error extracted from it.
73*1fa6dee9SAndroid Build Coastguard Worker//
74*1fa6dee9SAndroid Build Coastguard Worker// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool
75*1fa6dee9SAndroid Build Coastguard Worker// values, replacing pointers to booleans or strings whether they are nil or not, and recursing into
76*1fa6dee9SAndroid Build Coastguard Worker// embedded structs, pointers to structs, and interfaces containing
77*1fa6dee9SAndroid Build Coastguard Worker// pointers to structs.  Appending the zero value of a property will always be a no-op.
78*1fa6dee9SAndroid Build Coastguard Workerfunc AppendMatchingProperties(dst []interface{}, src interface{},
79*1fa6dee9SAndroid Build Coastguard Worker	filter ExtendPropertyFilterFunc) error {
80*1fa6dee9SAndroid Build Coastguard Worker	return extendMatchingProperties(dst, src, filter, OrderAppend)
81*1fa6dee9SAndroid Build Coastguard Worker}
82*1fa6dee9SAndroid Build Coastguard Worker
83*1fa6dee9SAndroid Build Coastguard Worker// PrependMatchingProperties prepends the values of properties in the property struct src to the
84*1fa6dee9SAndroid Build Coastguard Worker// property structs in dst.  dst and src do not have to be the same type, but every property in src
85*1fa6dee9SAndroid Build Coastguard Worker// must be found in at least one property in dst.  dst must be a slice of pointers to structs, and
86*1fa6dee9SAndroid Build Coastguard Worker// src must be a pointer to a struct.  Properties tagged `blueprint:"mutated"` are skipped.
87*1fa6dee9SAndroid Build Coastguard Worker//
88*1fa6dee9SAndroid Build Coastguard Worker// The filter function can prevent individual properties from being prepended by returning false, or
89*1fa6dee9SAndroid Build Coastguard Worker// abort PrependProperties with an error by returning an error.  Passing nil for filter will prepend
90*1fa6dee9SAndroid Build Coastguard Worker// all properties.
91*1fa6dee9SAndroid Build Coastguard Worker//
92*1fa6dee9SAndroid Build Coastguard Worker// An error returned by PrependProperties that applies to a specific property will be an
93*1fa6dee9SAndroid Build Coastguard Worker// *ExtendPropertyError, and can have the property name and error extracted from it.
94*1fa6dee9SAndroid Build Coastguard Worker//
95*1fa6dee9SAndroid Build Coastguard Worker// The prepend operation is defined as prepending strings, and slices of strings normally, OR-ing
96*1fa6dee9SAndroid Build Coastguard Worker// bool values, replacing nil pointers to booleans or strings, and recursing into
97*1fa6dee9SAndroid Build Coastguard Worker// embedded structs, pointers to structs, and interfaces containing
98*1fa6dee9SAndroid Build Coastguard Worker// pointers to structs.  Prepending the zero value of a property will always be a no-op.
99*1fa6dee9SAndroid Build Coastguard Workerfunc PrependMatchingProperties(dst []interface{}, src interface{},
100*1fa6dee9SAndroid Build Coastguard Worker	filter ExtendPropertyFilterFunc) error {
101*1fa6dee9SAndroid Build Coastguard Worker	return extendMatchingProperties(dst, src, filter, OrderPrepend)
102*1fa6dee9SAndroid Build Coastguard Worker}
103*1fa6dee9SAndroid Build Coastguard Worker
104*1fa6dee9SAndroid Build Coastguard Worker// ExtendProperties appends or prepends the values of properties in the property struct src to the
105*1fa6dee9SAndroid Build Coastguard Worker// property struct dst. dst and src must be the same type, and both must be pointers to structs.
106*1fa6dee9SAndroid Build Coastguard Worker// Properties tagged `blueprint:"mutated"` are skipped.
107*1fa6dee9SAndroid Build Coastguard Worker//
108*1fa6dee9SAndroid Build Coastguard Worker// The filter function can prevent individual properties from being appended or prepended by
109*1fa6dee9SAndroid Build Coastguard Worker// returning false, or abort ExtendProperties with an error by returning an error.  Passing nil for
110*1fa6dee9SAndroid Build Coastguard Worker// filter will append or prepend all properties.
111*1fa6dee9SAndroid Build Coastguard Worker//
112*1fa6dee9SAndroid Build Coastguard Worker// The order function is called on each non-filtered property to determine if it should be appended
113*1fa6dee9SAndroid Build Coastguard Worker// or prepended.
114*1fa6dee9SAndroid Build Coastguard Worker//
115*1fa6dee9SAndroid Build Coastguard Worker// An error returned by ExtendProperties that applies to a specific property will be an
116*1fa6dee9SAndroid Build Coastguard Worker// *ExtendPropertyError, and can have the property name and error extracted from it.
117*1fa6dee9SAndroid Build Coastguard Worker//
118*1fa6dee9SAndroid Build Coastguard Worker// The append operation is defined as appending strings and slices of strings normally, OR-ing bool
119*1fa6dee9SAndroid Build Coastguard Worker// values, replacing non-nil pointers to booleans or strings, and recursing into
120*1fa6dee9SAndroid Build Coastguard Worker// embedded structs, pointers to structs, and interfaces containing
121*1fa6dee9SAndroid Build Coastguard Worker// pointers to structs.  Appending or prepending the zero value of a property will always be a
122*1fa6dee9SAndroid Build Coastguard Worker// no-op.
123*1fa6dee9SAndroid Build Coastguard Workerfunc ExtendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc,
124*1fa6dee9SAndroid Build Coastguard Worker	order ExtendPropertyOrderFunc) error {
125*1fa6dee9SAndroid Build Coastguard Worker	return extendProperties(dst, src, filter, order)
126*1fa6dee9SAndroid Build Coastguard Worker}
127*1fa6dee9SAndroid Build Coastguard Worker
128*1fa6dee9SAndroid Build Coastguard Worker// ExtendMatchingProperties appends or prepends the values of properties in the property struct src
129*1fa6dee9SAndroid Build Coastguard Worker// to the property structs in dst.  dst and src do not have to be the same type, but every property
130*1fa6dee9SAndroid Build Coastguard Worker// in src must be found in at least one property in dst.  dst must be a slice of pointers to
131*1fa6dee9SAndroid Build Coastguard Worker// structs, and src must be a pointer to a struct.  Properties tagged `blueprint:"mutated"` are
132*1fa6dee9SAndroid Build Coastguard Worker// skipped.
133*1fa6dee9SAndroid Build Coastguard Worker//
134*1fa6dee9SAndroid Build Coastguard Worker// The filter function can prevent individual properties from being appended or prepended by
135*1fa6dee9SAndroid Build Coastguard Worker// returning false, or abort ExtendMatchingProperties with an error by returning an error.  Passing
136*1fa6dee9SAndroid Build Coastguard Worker// nil for filter will append or prepend all properties.
137*1fa6dee9SAndroid Build Coastguard Worker//
138*1fa6dee9SAndroid Build Coastguard Worker// The order function is called on each non-filtered property to determine if it should be appended
139*1fa6dee9SAndroid Build Coastguard Worker// or prepended.
140*1fa6dee9SAndroid Build Coastguard Worker//
141*1fa6dee9SAndroid Build Coastguard Worker// An error returned by ExtendMatchingProperties that applies to a specific property will be an
142*1fa6dee9SAndroid Build Coastguard Worker// *ExtendPropertyError, and can have the property name and error extracted from it.
143*1fa6dee9SAndroid Build Coastguard Worker//
144*1fa6dee9SAndroid Build Coastguard Worker// The append operation is defined as appending strings, and slices of strings normally, OR-ing bool
145*1fa6dee9SAndroid Build Coastguard Worker// values, replacing non-nil pointers to booleans or strings, and recursing into
146*1fa6dee9SAndroid Build Coastguard Worker// embedded structs, pointers to structs, and interfaces containing
147*1fa6dee9SAndroid Build Coastguard Worker// pointers to structs.  Appending or prepending the zero value of a property will always be a
148*1fa6dee9SAndroid Build Coastguard Worker// no-op.
149*1fa6dee9SAndroid Build Coastguard Workerfunc ExtendMatchingProperties(dst []interface{}, src interface{},
150*1fa6dee9SAndroid Build Coastguard Worker	filter ExtendPropertyFilterFunc, order ExtendPropertyOrderFunc) error {
151*1fa6dee9SAndroid Build Coastguard Worker	return extendMatchingProperties(dst, src, filter, order)
152*1fa6dee9SAndroid Build Coastguard Worker}
153*1fa6dee9SAndroid Build Coastguard Worker
154*1fa6dee9SAndroid Build Coastguard Workertype Order int
155*1fa6dee9SAndroid Build Coastguard Worker
156*1fa6dee9SAndroid Build Coastguard Workerconst (
157*1fa6dee9SAndroid Build Coastguard Worker	// When merging properties, strings and lists will be concatenated, and booleans will be OR'd together
158*1fa6dee9SAndroid Build Coastguard Worker	Append Order = iota
159*1fa6dee9SAndroid Build Coastguard Worker	// Same as append, but acts as if the arguments to the extend* functions were swapped. The src value will be
160*1fa6dee9SAndroid Build Coastguard Worker	// prepended to the dst value instead of appended.
161*1fa6dee9SAndroid Build Coastguard Worker	Prepend
162*1fa6dee9SAndroid Build Coastguard Worker	// Instead of concatenating/ORing properties, the dst value will be completely replaced by the src value.
163*1fa6dee9SAndroid Build Coastguard Worker	// Replace currently only works for slices, maps, and configurable properties. Due to legacy behavior,
164*1fa6dee9SAndroid Build Coastguard Worker	// pointer properties will always act as if they're using replace ordering.
165*1fa6dee9SAndroid Build Coastguard Worker	Replace
166*1fa6dee9SAndroid Build Coastguard Worker	// Same as replace, but acts as if the arguments to the extend* functions were swapped. The src value will be
167*1fa6dee9SAndroid Build Coastguard Worker	// used only if the dst value was unset.
168*1fa6dee9SAndroid Build Coastguard Worker	Prepend_replace
169*1fa6dee9SAndroid Build Coastguard Worker)
170*1fa6dee9SAndroid Build Coastguard Worker
171*1fa6dee9SAndroid Build Coastguard Workertype ExtendPropertyFilterFunc func(dstField, srcField reflect.StructField) (bool, error)
172*1fa6dee9SAndroid Build Coastguard Worker
173*1fa6dee9SAndroid Build Coastguard Workertype ExtendPropertyOrderFunc func(dstField, srcField reflect.StructField) (Order, error)
174*1fa6dee9SAndroid Build Coastguard Worker
175*1fa6dee9SAndroid Build Coastguard Workerfunc OrderAppend(dstField, srcField reflect.StructField) (Order, error) {
176*1fa6dee9SAndroid Build Coastguard Worker	return Append, nil
177*1fa6dee9SAndroid Build Coastguard Worker}
178*1fa6dee9SAndroid Build Coastguard Worker
179*1fa6dee9SAndroid Build Coastguard Workerfunc OrderPrepend(dstField, srcField reflect.StructField) (Order, error) {
180*1fa6dee9SAndroid Build Coastguard Worker	return Prepend, nil
181*1fa6dee9SAndroid Build Coastguard Worker}
182*1fa6dee9SAndroid Build Coastguard Worker
183*1fa6dee9SAndroid Build Coastguard Workerfunc OrderReplace(dstField, srcField reflect.StructField) (Order, error) {
184*1fa6dee9SAndroid Build Coastguard Worker	return Replace, nil
185*1fa6dee9SAndroid Build Coastguard Worker}
186*1fa6dee9SAndroid Build Coastguard Worker
187*1fa6dee9SAndroid Build Coastguard Workertype ExtendPropertyError struct {
188*1fa6dee9SAndroid Build Coastguard Worker	Err      error
189*1fa6dee9SAndroid Build Coastguard Worker	Property string
190*1fa6dee9SAndroid Build Coastguard Worker}
191*1fa6dee9SAndroid Build Coastguard Worker
192*1fa6dee9SAndroid Build Coastguard Workerfunc (e *ExtendPropertyError) Error() string {
193*1fa6dee9SAndroid Build Coastguard Worker	return fmt.Sprintf("can't extend property %q: %s", e.Property, e.Err)
194*1fa6dee9SAndroid Build Coastguard Worker}
195*1fa6dee9SAndroid Build Coastguard Worker
196*1fa6dee9SAndroid Build Coastguard Workerfunc extendPropertyErrorf(property string, format string, a ...interface{}) *ExtendPropertyError {
197*1fa6dee9SAndroid Build Coastguard Worker	return &ExtendPropertyError{
198*1fa6dee9SAndroid Build Coastguard Worker		Err:      fmt.Errorf(format, a...),
199*1fa6dee9SAndroid Build Coastguard Worker		Property: property,
200*1fa6dee9SAndroid Build Coastguard Worker	}
201*1fa6dee9SAndroid Build Coastguard Worker}
202*1fa6dee9SAndroid Build Coastguard Worker
203*1fa6dee9SAndroid Build Coastguard Workerfunc extendProperties(dst interface{}, src interface{}, filter ExtendPropertyFilterFunc,
204*1fa6dee9SAndroid Build Coastguard Worker	order ExtendPropertyOrderFunc) error {
205*1fa6dee9SAndroid Build Coastguard Worker
206*1fa6dee9SAndroid Build Coastguard Worker	srcValue, err := getStruct(src)
207*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
208*1fa6dee9SAndroid Build Coastguard Worker		if _, ok := err.(getStructEmptyError); ok {
209*1fa6dee9SAndroid Build Coastguard Worker			return nil
210*1fa6dee9SAndroid Build Coastguard Worker		}
211*1fa6dee9SAndroid Build Coastguard Worker		return err
212*1fa6dee9SAndroid Build Coastguard Worker	}
213*1fa6dee9SAndroid Build Coastguard Worker
214*1fa6dee9SAndroid Build Coastguard Worker	dstValue, err := getOrCreateStruct(dst)
215*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
216*1fa6dee9SAndroid Build Coastguard Worker		return err
217*1fa6dee9SAndroid Build Coastguard Worker	}
218*1fa6dee9SAndroid Build Coastguard Worker
219*1fa6dee9SAndroid Build Coastguard Worker	if dstValue.Type() != srcValue.Type() {
220*1fa6dee9SAndroid Build Coastguard Worker		return fmt.Errorf("expected matching types for dst and src, got %T and %T", dst, src)
221*1fa6dee9SAndroid Build Coastguard Worker	}
222*1fa6dee9SAndroid Build Coastguard Worker
223*1fa6dee9SAndroid Build Coastguard Worker	dstValues := []reflect.Value{dstValue}
224*1fa6dee9SAndroid Build Coastguard Worker
225*1fa6dee9SAndroid Build Coastguard Worker	return extendPropertiesRecursive(dstValues, srcValue, make([]string, 0, 8), filter, true, order)
226*1fa6dee9SAndroid Build Coastguard Worker}
227*1fa6dee9SAndroid Build Coastguard Worker
228*1fa6dee9SAndroid Build Coastguard Workerfunc extendMatchingProperties(dst []interface{}, src interface{}, filter ExtendPropertyFilterFunc,
229*1fa6dee9SAndroid Build Coastguard Worker	order ExtendPropertyOrderFunc) error {
230*1fa6dee9SAndroid Build Coastguard Worker
231*1fa6dee9SAndroid Build Coastguard Worker	srcValue, err := getStruct(src)
232*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
233*1fa6dee9SAndroid Build Coastguard Worker		if _, ok := err.(getStructEmptyError); ok {
234*1fa6dee9SAndroid Build Coastguard Worker			return nil
235*1fa6dee9SAndroid Build Coastguard Worker		}
236*1fa6dee9SAndroid Build Coastguard Worker		return err
237*1fa6dee9SAndroid Build Coastguard Worker	}
238*1fa6dee9SAndroid Build Coastguard Worker
239*1fa6dee9SAndroid Build Coastguard Worker	dstValues := make([]reflect.Value, len(dst))
240*1fa6dee9SAndroid Build Coastguard Worker	for i := range dst {
241*1fa6dee9SAndroid Build Coastguard Worker		var err error
242*1fa6dee9SAndroid Build Coastguard Worker		dstValues[i], err = getOrCreateStruct(dst[i])
243*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
244*1fa6dee9SAndroid Build Coastguard Worker			return err
245*1fa6dee9SAndroid Build Coastguard Worker		}
246*1fa6dee9SAndroid Build Coastguard Worker	}
247*1fa6dee9SAndroid Build Coastguard Worker
248*1fa6dee9SAndroid Build Coastguard Worker	return extendPropertiesRecursive(dstValues, srcValue, make([]string, 0, 8), filter, false, order)
249*1fa6dee9SAndroid Build Coastguard Worker}
250*1fa6dee9SAndroid Build Coastguard Worker
251*1fa6dee9SAndroid Build Coastguard Workerfunc extendPropertiesRecursive(dstValues []reflect.Value, srcValue reflect.Value,
252*1fa6dee9SAndroid Build Coastguard Worker	prefix []string, filter ExtendPropertyFilterFunc, sameTypes bool,
253*1fa6dee9SAndroid Build Coastguard Worker	orderFunc ExtendPropertyOrderFunc) error {
254*1fa6dee9SAndroid Build Coastguard Worker
255*1fa6dee9SAndroid Build Coastguard Worker	dstValuesCopied := false
256*1fa6dee9SAndroid Build Coastguard Worker
257*1fa6dee9SAndroid Build Coastguard Worker	propertyName := func(field reflect.StructField) string {
258*1fa6dee9SAndroid Build Coastguard Worker		names := make([]string, 0, len(prefix)+1)
259*1fa6dee9SAndroid Build Coastguard Worker		for _, s := range prefix {
260*1fa6dee9SAndroid Build Coastguard Worker			names = append(names, PropertyNameForField(s))
261*1fa6dee9SAndroid Build Coastguard Worker		}
262*1fa6dee9SAndroid Build Coastguard Worker		names = append(names, PropertyNameForField(field.Name))
263*1fa6dee9SAndroid Build Coastguard Worker		return strings.Join(names, ".")
264*1fa6dee9SAndroid Build Coastguard Worker	}
265*1fa6dee9SAndroid Build Coastguard Worker
266*1fa6dee9SAndroid Build Coastguard Worker	srcType := srcValue.Type()
267*1fa6dee9SAndroid Build Coastguard Worker	for i, srcField := range typeFields(srcType) {
268*1fa6dee9SAndroid Build Coastguard Worker		if ShouldSkipProperty(srcField) {
269*1fa6dee9SAndroid Build Coastguard Worker			continue
270*1fa6dee9SAndroid Build Coastguard Worker		}
271*1fa6dee9SAndroid Build Coastguard Worker
272*1fa6dee9SAndroid Build Coastguard Worker		srcFieldValue := srcValue.Field(i)
273*1fa6dee9SAndroid Build Coastguard Worker
274*1fa6dee9SAndroid Build Coastguard Worker		// Step into source interfaces
275*1fa6dee9SAndroid Build Coastguard Worker		if srcFieldValue.Kind() == reflect.Interface {
276*1fa6dee9SAndroid Build Coastguard Worker			if srcFieldValue.IsNil() {
277*1fa6dee9SAndroid Build Coastguard Worker				continue
278*1fa6dee9SAndroid Build Coastguard Worker			}
279*1fa6dee9SAndroid Build Coastguard Worker
280*1fa6dee9SAndroid Build Coastguard Worker			srcFieldValue = srcFieldValue.Elem()
281*1fa6dee9SAndroid Build Coastguard Worker
282*1fa6dee9SAndroid Build Coastguard Worker			if srcFieldValue.Kind() != reflect.Ptr {
283*1fa6dee9SAndroid Build Coastguard Worker				return extendPropertyErrorf(propertyName(srcField), "interface not a pointer")
284*1fa6dee9SAndroid Build Coastguard Worker			}
285*1fa6dee9SAndroid Build Coastguard Worker		}
286*1fa6dee9SAndroid Build Coastguard Worker
287*1fa6dee9SAndroid Build Coastguard Worker		// Step into source pointers to structs
288*1fa6dee9SAndroid Build Coastguard Worker		if isStructPtr(srcFieldValue.Type()) {
289*1fa6dee9SAndroid Build Coastguard Worker			if srcFieldValue.IsNil() {
290*1fa6dee9SAndroid Build Coastguard Worker				continue
291*1fa6dee9SAndroid Build Coastguard Worker			}
292*1fa6dee9SAndroid Build Coastguard Worker
293*1fa6dee9SAndroid Build Coastguard Worker			srcFieldValue = srcFieldValue.Elem()
294*1fa6dee9SAndroid Build Coastguard Worker		}
295*1fa6dee9SAndroid Build Coastguard Worker
296*1fa6dee9SAndroid Build Coastguard Worker		found := false
297*1fa6dee9SAndroid Build Coastguard Worker		var recurse []reflect.Value
298*1fa6dee9SAndroid Build Coastguard Worker		// Use an iteration loop so elements can be added to the end of dstValues inside the loop.
299*1fa6dee9SAndroid Build Coastguard Worker		for j := 0; j < len(dstValues); j++ {
300*1fa6dee9SAndroid Build Coastguard Worker			dstValue := dstValues[j]
301*1fa6dee9SAndroid Build Coastguard Worker			dstType := dstValue.Type()
302*1fa6dee9SAndroid Build Coastguard Worker			var dstField reflect.StructField
303*1fa6dee9SAndroid Build Coastguard Worker
304*1fa6dee9SAndroid Build Coastguard Worker			dstFields := typeFields(dstType)
305*1fa6dee9SAndroid Build Coastguard Worker			if dstType == srcType {
306*1fa6dee9SAndroid Build Coastguard Worker				dstField = dstFields[i]
307*1fa6dee9SAndroid Build Coastguard Worker			} else {
308*1fa6dee9SAndroid Build Coastguard Worker				var ok bool
309*1fa6dee9SAndroid Build Coastguard Worker				for _, field := range dstFields {
310*1fa6dee9SAndroid Build Coastguard Worker					if field.Name == srcField.Name {
311*1fa6dee9SAndroid Build Coastguard Worker						dstField = field
312*1fa6dee9SAndroid Build Coastguard Worker						ok = true
313*1fa6dee9SAndroid Build Coastguard Worker					} else if IsEmbedded(field) {
314*1fa6dee9SAndroid Build Coastguard Worker						embeddedDstValue := dstValue.FieldByIndex(field.Index)
315*1fa6dee9SAndroid Build Coastguard Worker						if isStructPtr(embeddedDstValue.Type()) {
316*1fa6dee9SAndroid Build Coastguard Worker							if embeddedDstValue.IsNil() {
317*1fa6dee9SAndroid Build Coastguard Worker								newEmbeddedDstValue := reflect.New(embeddedDstValue.Type().Elem())
318*1fa6dee9SAndroid Build Coastguard Worker								embeddedDstValue.Set(newEmbeddedDstValue)
319*1fa6dee9SAndroid Build Coastguard Worker							}
320*1fa6dee9SAndroid Build Coastguard Worker							embeddedDstValue = embeddedDstValue.Elem()
321*1fa6dee9SAndroid Build Coastguard Worker						}
322*1fa6dee9SAndroid Build Coastguard Worker						if !isStruct(embeddedDstValue.Type()) {
323*1fa6dee9SAndroid Build Coastguard Worker							return extendPropertyErrorf(propertyName(srcField), "%s is not a struct (%s)",
324*1fa6dee9SAndroid Build Coastguard Worker								propertyName(field), embeddedDstValue.Type())
325*1fa6dee9SAndroid Build Coastguard Worker						}
326*1fa6dee9SAndroid Build Coastguard Worker						// The destination struct contains an embedded struct, add it to the list
327*1fa6dee9SAndroid Build Coastguard Worker						// of destinations to consider.  Make a copy of dstValues if necessary
328*1fa6dee9SAndroid Build Coastguard Worker						// to avoid modifying the backing array of an input parameter.
329*1fa6dee9SAndroid Build Coastguard Worker						if !dstValuesCopied {
330*1fa6dee9SAndroid Build Coastguard Worker							dstValues = slices.Clone(dstValues)
331*1fa6dee9SAndroid Build Coastguard Worker							dstValuesCopied = true
332*1fa6dee9SAndroid Build Coastguard Worker						}
333*1fa6dee9SAndroid Build Coastguard Worker						dstValues = append(dstValues, embeddedDstValue)
334*1fa6dee9SAndroid Build Coastguard Worker					}
335*1fa6dee9SAndroid Build Coastguard Worker				}
336*1fa6dee9SAndroid Build Coastguard Worker				if !ok {
337*1fa6dee9SAndroid Build Coastguard Worker					continue
338*1fa6dee9SAndroid Build Coastguard Worker				}
339*1fa6dee9SAndroid Build Coastguard Worker			}
340*1fa6dee9SAndroid Build Coastguard Worker
341*1fa6dee9SAndroid Build Coastguard Worker			found = true
342*1fa6dee9SAndroid Build Coastguard Worker
343*1fa6dee9SAndroid Build Coastguard Worker			dstFieldValue := dstValue.FieldByIndex(dstField.Index)
344*1fa6dee9SAndroid Build Coastguard Worker			origDstFieldValue := dstFieldValue
345*1fa6dee9SAndroid Build Coastguard Worker
346*1fa6dee9SAndroid Build Coastguard Worker			// Step into destination interfaces
347*1fa6dee9SAndroid Build Coastguard Worker			if dstFieldValue.Kind() == reflect.Interface {
348*1fa6dee9SAndroid Build Coastguard Worker				if dstFieldValue.IsNil() {
349*1fa6dee9SAndroid Build Coastguard Worker					return extendPropertyErrorf(propertyName(srcField), "nilitude mismatch")
350*1fa6dee9SAndroid Build Coastguard Worker				}
351*1fa6dee9SAndroid Build Coastguard Worker
352*1fa6dee9SAndroid Build Coastguard Worker				dstFieldValue = dstFieldValue.Elem()
353*1fa6dee9SAndroid Build Coastguard Worker
354*1fa6dee9SAndroid Build Coastguard Worker				if dstFieldValue.Kind() != reflect.Ptr {
355*1fa6dee9SAndroid Build Coastguard Worker					return extendPropertyErrorf(propertyName(srcField), "interface not a pointer")
356*1fa6dee9SAndroid Build Coastguard Worker				}
357*1fa6dee9SAndroid Build Coastguard Worker			}
358*1fa6dee9SAndroid Build Coastguard Worker
359*1fa6dee9SAndroid Build Coastguard Worker			// Step into destination pointers to structs
360*1fa6dee9SAndroid Build Coastguard Worker			if isStructPtr(dstFieldValue.Type()) {
361*1fa6dee9SAndroid Build Coastguard Worker				if dstFieldValue.IsNil() {
362*1fa6dee9SAndroid Build Coastguard Worker					dstFieldValue = reflect.New(dstFieldValue.Type().Elem())
363*1fa6dee9SAndroid Build Coastguard Worker					origDstFieldValue.Set(dstFieldValue)
364*1fa6dee9SAndroid Build Coastguard Worker				}
365*1fa6dee9SAndroid Build Coastguard Worker
366*1fa6dee9SAndroid Build Coastguard Worker				dstFieldValue = dstFieldValue.Elem()
367*1fa6dee9SAndroid Build Coastguard Worker			}
368*1fa6dee9SAndroid Build Coastguard Worker
369*1fa6dee9SAndroid Build Coastguard Worker			switch srcFieldValue.Kind() {
370*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Struct:
371*1fa6dee9SAndroid Build Coastguard Worker				if isConfigurable(srcField.Type) {
372*1fa6dee9SAndroid Build Coastguard Worker					if srcFieldValue.Type() != dstFieldValue.Type() {
373*1fa6dee9SAndroid Build Coastguard Worker						return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
374*1fa6dee9SAndroid Build Coastguard Worker							dstFieldValue.Type(), srcFieldValue.Type())
375*1fa6dee9SAndroid Build Coastguard Worker					}
376*1fa6dee9SAndroid Build Coastguard Worker				} else {
377*1fa6dee9SAndroid Build Coastguard Worker					if sameTypes && dstFieldValue.Type() != srcFieldValue.Type() {
378*1fa6dee9SAndroid Build Coastguard Worker						return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
379*1fa6dee9SAndroid Build Coastguard Worker							dstFieldValue.Type(), srcFieldValue.Type())
380*1fa6dee9SAndroid Build Coastguard Worker					}
381*1fa6dee9SAndroid Build Coastguard Worker
382*1fa6dee9SAndroid Build Coastguard Worker					// Recursively extend the struct's fields.
383*1fa6dee9SAndroid Build Coastguard Worker					recurse = append(recurse, dstFieldValue)
384*1fa6dee9SAndroid Build Coastguard Worker					continue
385*1fa6dee9SAndroid Build Coastguard Worker				}
386*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Bool, reflect.String, reflect.Slice, reflect.Map:
387*1fa6dee9SAndroid Build Coastguard Worker				// If the types don't match or srcFieldValue cannot be converted to a Configurable type, it's an error
388*1fa6dee9SAndroid Build Coastguard Worker				ct, err := configurableType(srcFieldValue.Type())
389*1fa6dee9SAndroid Build Coastguard Worker				if srcFieldValue.Type() != dstFieldValue.Type() && (err != nil || dstFieldValue.Type() != ct) {
390*1fa6dee9SAndroid Build Coastguard Worker					return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
391*1fa6dee9SAndroid Build Coastguard Worker						dstFieldValue.Type(), srcFieldValue.Type())
392*1fa6dee9SAndroid Build Coastguard Worker				}
393*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Ptr:
394*1fa6dee9SAndroid Build Coastguard Worker				// If the types don't match or srcFieldValue cannot be converted to a Configurable type, it's an error
395*1fa6dee9SAndroid Build Coastguard Worker				ct, err := configurableType(srcFieldValue.Type().Elem())
396*1fa6dee9SAndroid Build Coastguard Worker				if srcFieldValue.Type() != dstFieldValue.Type() && (err != nil || dstFieldValue.Type() != ct) {
397*1fa6dee9SAndroid Build Coastguard Worker					return extendPropertyErrorf(propertyName(srcField), "mismatched types %s and %s",
398*1fa6dee9SAndroid Build Coastguard Worker						dstFieldValue.Type(), srcFieldValue.Type())
399*1fa6dee9SAndroid Build Coastguard Worker				}
400*1fa6dee9SAndroid Build Coastguard Worker				switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind {
401*1fa6dee9SAndroid Build Coastguard Worker				case reflect.Bool, reflect.Int64, reflect.String, reflect.Struct:
402*1fa6dee9SAndroid Build Coastguard Worker				// Nothing
403*1fa6dee9SAndroid Build Coastguard Worker				default:
404*1fa6dee9SAndroid Build Coastguard Worker					return extendPropertyErrorf(propertyName(srcField), "pointer is a %s", ptrKind)
405*1fa6dee9SAndroid Build Coastguard Worker				}
406*1fa6dee9SAndroid Build Coastguard Worker			default:
407*1fa6dee9SAndroid Build Coastguard Worker				return extendPropertyErrorf(propertyName(srcField), "unsupported kind %s",
408*1fa6dee9SAndroid Build Coastguard Worker					srcFieldValue.Kind())
409*1fa6dee9SAndroid Build Coastguard Worker			}
410*1fa6dee9SAndroid Build Coastguard Worker
411*1fa6dee9SAndroid Build Coastguard Worker			if filter != nil {
412*1fa6dee9SAndroid Build Coastguard Worker				b, err := filter(dstField, srcField)
413*1fa6dee9SAndroid Build Coastguard Worker				if err != nil {
414*1fa6dee9SAndroid Build Coastguard Worker					return &ExtendPropertyError{
415*1fa6dee9SAndroid Build Coastguard Worker						Property: propertyName(srcField),
416*1fa6dee9SAndroid Build Coastguard Worker						Err:      err,
417*1fa6dee9SAndroid Build Coastguard Worker					}
418*1fa6dee9SAndroid Build Coastguard Worker				}
419*1fa6dee9SAndroid Build Coastguard Worker				if !b {
420*1fa6dee9SAndroid Build Coastguard Worker					continue
421*1fa6dee9SAndroid Build Coastguard Worker				}
422*1fa6dee9SAndroid Build Coastguard Worker			}
423*1fa6dee9SAndroid Build Coastguard Worker
424*1fa6dee9SAndroid Build Coastguard Worker			order := Append
425*1fa6dee9SAndroid Build Coastguard Worker			if orderFunc != nil {
426*1fa6dee9SAndroid Build Coastguard Worker				var err error
427*1fa6dee9SAndroid Build Coastguard Worker				order, err = orderFunc(dstField, srcField)
428*1fa6dee9SAndroid Build Coastguard Worker				if err != nil {
429*1fa6dee9SAndroid Build Coastguard Worker					return &ExtendPropertyError{
430*1fa6dee9SAndroid Build Coastguard Worker						Property: propertyName(srcField),
431*1fa6dee9SAndroid Build Coastguard Worker						Err:      err,
432*1fa6dee9SAndroid Build Coastguard Worker					}
433*1fa6dee9SAndroid Build Coastguard Worker				}
434*1fa6dee9SAndroid Build Coastguard Worker			}
435*1fa6dee9SAndroid Build Coastguard Worker
436*1fa6dee9SAndroid Build Coastguard Worker			if HasTag(dstField, "android", "replace_instead_of_append") {
437*1fa6dee9SAndroid Build Coastguard Worker				if order == Append {
438*1fa6dee9SAndroid Build Coastguard Worker					order = Replace
439*1fa6dee9SAndroid Build Coastguard Worker				} else if order == Prepend {
440*1fa6dee9SAndroid Build Coastguard Worker					order = Prepend_replace
441*1fa6dee9SAndroid Build Coastguard Worker				}
442*1fa6dee9SAndroid Build Coastguard Worker			}
443*1fa6dee9SAndroid Build Coastguard Worker
444*1fa6dee9SAndroid Build Coastguard Worker			ExtendBasicType(dstFieldValue, srcFieldValue, order)
445*1fa6dee9SAndroid Build Coastguard Worker		}
446*1fa6dee9SAndroid Build Coastguard Worker
447*1fa6dee9SAndroid Build Coastguard Worker		if len(recurse) > 0 {
448*1fa6dee9SAndroid Build Coastguard Worker			err := extendPropertiesRecursive(recurse, srcFieldValue,
449*1fa6dee9SAndroid Build Coastguard Worker				append(prefix, srcField.Name), filter, sameTypes, orderFunc)
450*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
451*1fa6dee9SAndroid Build Coastguard Worker				return err
452*1fa6dee9SAndroid Build Coastguard Worker			}
453*1fa6dee9SAndroid Build Coastguard Worker		} else if !found {
454*1fa6dee9SAndroid Build Coastguard Worker			return extendPropertyErrorf(propertyName(srcField), "failed to find property to extend")
455*1fa6dee9SAndroid Build Coastguard Worker		}
456*1fa6dee9SAndroid Build Coastguard Worker	}
457*1fa6dee9SAndroid Build Coastguard Worker
458*1fa6dee9SAndroid Build Coastguard Worker	return nil
459*1fa6dee9SAndroid Build Coastguard Worker}
460*1fa6dee9SAndroid Build Coastguard Worker
461*1fa6dee9SAndroid Build Coastguard Workerfunc ExtendBasicType(dstFieldValue, srcFieldValue reflect.Value, order Order) {
462*1fa6dee9SAndroid Build Coastguard Worker	prepend := order == Prepend || order == Prepend_replace
463*1fa6dee9SAndroid Build Coastguard Worker
464*1fa6dee9SAndroid Build Coastguard Worker	if !srcFieldValue.IsValid() {
465*1fa6dee9SAndroid Build Coastguard Worker		return
466*1fa6dee9SAndroid Build Coastguard Worker	}
467*1fa6dee9SAndroid Build Coastguard Worker
468*1fa6dee9SAndroid Build Coastguard Worker	// If dst is a Configurable and src isn't, promote src to a Configurable.
469*1fa6dee9SAndroid Build Coastguard Worker	// This isn't necessary if all property structs are using Configurable values,
470*1fa6dee9SAndroid Build Coastguard Worker	// but it's helpful to avoid having to change as many places in the code when
471*1fa6dee9SAndroid Build Coastguard Worker	// converting properties to Configurable properties. For example, load hooks
472*1fa6dee9SAndroid Build Coastguard Worker	// make their own mini-property structs and append them onto the main property
473*1fa6dee9SAndroid Build Coastguard Worker	// structs when they want to change the default values of properties.
474*1fa6dee9SAndroid Build Coastguard Worker	srcFieldType := srcFieldValue.Type()
475*1fa6dee9SAndroid Build Coastguard Worker	if isConfigurable(dstFieldValue.Type()) && !isConfigurable(srcFieldType) {
476*1fa6dee9SAndroid Build Coastguard Worker		srcFieldValue = promoteValueToConfigurable(srcFieldValue)
477*1fa6dee9SAndroid Build Coastguard Worker	}
478*1fa6dee9SAndroid Build Coastguard Worker
479*1fa6dee9SAndroid Build Coastguard Worker	switch srcFieldValue.Kind() {
480*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Struct:
481*1fa6dee9SAndroid Build Coastguard Worker		if !isConfigurable(srcFieldValue.Type()) {
482*1fa6dee9SAndroid Build Coastguard Worker			panic("Should be unreachable")
483*1fa6dee9SAndroid Build Coastguard Worker		}
484*1fa6dee9SAndroid Build Coastguard Worker		replace := order == Prepend_replace || order == Replace
485*1fa6dee9SAndroid Build Coastguard Worker		unpackedDst := dstFieldValue.Interface().(configurableReflection)
486*1fa6dee9SAndroid Build Coastguard Worker		if unpackedDst.isEmpty() {
487*1fa6dee9SAndroid Build Coastguard Worker			// Properties that were never initialized via unpacking from a bp file value
488*1fa6dee9SAndroid Build Coastguard Worker			// will have a nil inner value, making them unable to be modified without a pointer
489*1fa6dee9SAndroid Build Coastguard Worker			// like we don't have here. So instead replace the whole configurable object.
490*1fa6dee9SAndroid Build Coastguard Worker			dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Interface().(configurableReflection).clone()))
491*1fa6dee9SAndroid Build Coastguard Worker		} else {
492*1fa6dee9SAndroid Build Coastguard Worker			unpackedDst.setAppend(srcFieldValue.Interface(), replace, prepend)
493*1fa6dee9SAndroid Build Coastguard Worker		}
494*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Bool:
495*1fa6dee9SAndroid Build Coastguard Worker		// Boolean OR
496*1fa6dee9SAndroid Build Coastguard Worker		dstFieldValue.Set(reflect.ValueOf(srcFieldValue.Bool() || dstFieldValue.Bool()))
497*1fa6dee9SAndroid Build Coastguard Worker	case reflect.String:
498*1fa6dee9SAndroid Build Coastguard Worker		if prepend {
499*1fa6dee9SAndroid Build Coastguard Worker			dstFieldValue.SetString(srcFieldValue.String() +
500*1fa6dee9SAndroid Build Coastguard Worker				dstFieldValue.String())
501*1fa6dee9SAndroid Build Coastguard Worker		} else {
502*1fa6dee9SAndroid Build Coastguard Worker			dstFieldValue.SetString(dstFieldValue.String() +
503*1fa6dee9SAndroid Build Coastguard Worker				srcFieldValue.String())
504*1fa6dee9SAndroid Build Coastguard Worker		}
505*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Slice:
506*1fa6dee9SAndroid Build Coastguard Worker		if srcFieldValue.IsNil() {
507*1fa6dee9SAndroid Build Coastguard Worker			break
508*1fa6dee9SAndroid Build Coastguard Worker		}
509*1fa6dee9SAndroid Build Coastguard Worker
510*1fa6dee9SAndroid Build Coastguard Worker		newSlice := reflect.MakeSlice(srcFieldValue.Type(), 0,
511*1fa6dee9SAndroid Build Coastguard Worker			dstFieldValue.Len()+srcFieldValue.Len())
512*1fa6dee9SAndroid Build Coastguard Worker		if prepend {
513*1fa6dee9SAndroid Build Coastguard Worker			newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
514*1fa6dee9SAndroid Build Coastguard Worker			newSlice = reflect.AppendSlice(newSlice, dstFieldValue)
515*1fa6dee9SAndroid Build Coastguard Worker		} else if order == Append {
516*1fa6dee9SAndroid Build Coastguard Worker			newSlice = reflect.AppendSlice(newSlice, dstFieldValue)
517*1fa6dee9SAndroid Build Coastguard Worker			newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
518*1fa6dee9SAndroid Build Coastguard Worker		} else {
519*1fa6dee9SAndroid Build Coastguard Worker			// replace
520*1fa6dee9SAndroid Build Coastguard Worker			newSlice = reflect.AppendSlice(newSlice, srcFieldValue)
521*1fa6dee9SAndroid Build Coastguard Worker		}
522*1fa6dee9SAndroid Build Coastguard Worker		dstFieldValue.Set(newSlice)
523*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Map:
524*1fa6dee9SAndroid Build Coastguard Worker		if srcFieldValue.IsNil() {
525*1fa6dee9SAndroid Build Coastguard Worker			break
526*1fa6dee9SAndroid Build Coastguard Worker		}
527*1fa6dee9SAndroid Build Coastguard Worker		var mapValue reflect.Value
528*1fa6dee9SAndroid Build Coastguard Worker		// for append/prepend, maintain keys from original value
529*1fa6dee9SAndroid Build Coastguard Worker		// for replace, replace entire map
530*1fa6dee9SAndroid Build Coastguard Worker		if order == Replace || dstFieldValue.IsNil() {
531*1fa6dee9SAndroid Build Coastguard Worker			mapValue = srcFieldValue
532*1fa6dee9SAndroid Build Coastguard Worker		} else {
533*1fa6dee9SAndroid Build Coastguard Worker			mapValue = dstFieldValue
534*1fa6dee9SAndroid Build Coastguard Worker
535*1fa6dee9SAndroid Build Coastguard Worker			iter := srcFieldValue.MapRange()
536*1fa6dee9SAndroid Build Coastguard Worker			for iter.Next() {
537*1fa6dee9SAndroid Build Coastguard Worker				dstValue := dstFieldValue.MapIndex(iter.Key())
538*1fa6dee9SAndroid Build Coastguard Worker				if prepend {
539*1fa6dee9SAndroid Build Coastguard Worker					// if the key exists in the map, keep the original value.
540*1fa6dee9SAndroid Build Coastguard Worker					if !dstValue.IsValid() {
541*1fa6dee9SAndroid Build Coastguard Worker						// otherwise, add the new value
542*1fa6dee9SAndroid Build Coastguard Worker						mapValue.SetMapIndex(iter.Key(), iter.Value())
543*1fa6dee9SAndroid Build Coastguard Worker					}
544*1fa6dee9SAndroid Build Coastguard Worker				} else {
545*1fa6dee9SAndroid Build Coastguard Worker					// For append, replace the original value.
546*1fa6dee9SAndroid Build Coastguard Worker					mapValue.SetMapIndex(iter.Key(), iter.Value())
547*1fa6dee9SAndroid Build Coastguard Worker				}
548*1fa6dee9SAndroid Build Coastguard Worker			}
549*1fa6dee9SAndroid Build Coastguard Worker		}
550*1fa6dee9SAndroid Build Coastguard Worker		dstFieldValue.Set(mapValue)
551*1fa6dee9SAndroid Build Coastguard Worker	case reflect.Ptr:
552*1fa6dee9SAndroid Build Coastguard Worker		if srcFieldValue.IsNil() {
553*1fa6dee9SAndroid Build Coastguard Worker			break
554*1fa6dee9SAndroid Build Coastguard Worker		}
555*1fa6dee9SAndroid Build Coastguard Worker
556*1fa6dee9SAndroid Build Coastguard Worker		switch ptrKind := srcFieldValue.Type().Elem().Kind(); ptrKind {
557*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Bool:
558*1fa6dee9SAndroid Build Coastguard Worker			if prepend {
559*1fa6dee9SAndroid Build Coastguard Worker				if dstFieldValue.IsNil() {
560*1fa6dee9SAndroid Build Coastguard Worker					dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool())))
561*1fa6dee9SAndroid Build Coastguard Worker				}
562*1fa6dee9SAndroid Build Coastguard Worker			} else {
563*1fa6dee9SAndroid Build Coastguard Worker				// For append, replace the original value.
564*1fa6dee9SAndroid Build Coastguard Worker				dstFieldValue.Set(reflect.ValueOf(BoolPtr(srcFieldValue.Elem().Bool())))
565*1fa6dee9SAndroid Build Coastguard Worker			}
566*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Int64:
567*1fa6dee9SAndroid Build Coastguard Worker			if prepend {
568*1fa6dee9SAndroid Build Coastguard Worker				if dstFieldValue.IsNil() {
569*1fa6dee9SAndroid Build Coastguard Worker					// Int() returns Int64
570*1fa6dee9SAndroid Build Coastguard Worker					dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int())))
571*1fa6dee9SAndroid Build Coastguard Worker				}
572*1fa6dee9SAndroid Build Coastguard Worker			} else {
573*1fa6dee9SAndroid Build Coastguard Worker				// For append, replace the original value.
574*1fa6dee9SAndroid Build Coastguard Worker				// Int() returns Int64
575*1fa6dee9SAndroid Build Coastguard Worker				dstFieldValue.Set(reflect.ValueOf(Int64Ptr(srcFieldValue.Elem().Int())))
576*1fa6dee9SAndroid Build Coastguard Worker			}
577*1fa6dee9SAndroid Build Coastguard Worker		case reflect.String:
578*1fa6dee9SAndroid Build Coastguard Worker			if prepend {
579*1fa6dee9SAndroid Build Coastguard Worker				if dstFieldValue.IsNil() {
580*1fa6dee9SAndroid Build Coastguard Worker					dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String())))
581*1fa6dee9SAndroid Build Coastguard Worker				}
582*1fa6dee9SAndroid Build Coastguard Worker			} else {
583*1fa6dee9SAndroid Build Coastguard Worker				// For append, replace the original value.
584*1fa6dee9SAndroid Build Coastguard Worker				dstFieldValue.Set(reflect.ValueOf(StringPtr(srcFieldValue.Elem().String())))
585*1fa6dee9SAndroid Build Coastguard Worker			}
586*1fa6dee9SAndroid Build Coastguard Worker		case reflect.Struct:
587*1fa6dee9SAndroid Build Coastguard Worker			srcFieldValue := srcFieldValue.Elem()
588*1fa6dee9SAndroid Build Coastguard Worker			if !isConfigurable(srcFieldValue.Type()) {
589*1fa6dee9SAndroid Build Coastguard Worker				panic("Should be unreachable")
590*1fa6dee9SAndroid Build Coastguard Worker			}
591*1fa6dee9SAndroid Build Coastguard Worker			panic("Don't use pointers to Configurable properties. All Configurable properties can be unset, " +
592*1fa6dee9SAndroid Build Coastguard Worker				"and the 'replacing' behavior can be accomplished with the `blueprint:\"replace_instead_of_append\" " +
593*1fa6dee9SAndroid Build Coastguard Worker				"struct field tag. There's no reason to have a pointer configurable property.")
594*1fa6dee9SAndroid Build Coastguard Worker		default:
595*1fa6dee9SAndroid Build Coastguard Worker			panic(fmt.Errorf("unexpected pointer kind %s", ptrKind))
596*1fa6dee9SAndroid Build Coastguard Worker		}
597*1fa6dee9SAndroid Build Coastguard Worker	}
598*1fa6dee9SAndroid Build Coastguard Worker}
599*1fa6dee9SAndroid Build Coastguard Worker
600*1fa6dee9SAndroid Build Coastguard Worker// ShouldSkipProperty indicates whether a property should be skipped in processing.
601*1fa6dee9SAndroid Build Coastguard Workerfunc ShouldSkipProperty(structField reflect.StructField) bool {
602*1fa6dee9SAndroid Build Coastguard Worker	return structField.PkgPath != "" || // The field is not exported so just skip it.
603*1fa6dee9SAndroid Build Coastguard Worker		HasTag(structField, "blueprint", "mutated") // The field is not settable in a blueprint file
604*1fa6dee9SAndroid Build Coastguard Worker}
605*1fa6dee9SAndroid Build Coastguard Worker
606*1fa6dee9SAndroid Build Coastguard Worker// IsEmbedded indicates whether a property is embedded. This is useful for determining nesting name
607*1fa6dee9SAndroid Build Coastguard Worker// as the name of the embedded field is _not_ used in blueprint files.
608*1fa6dee9SAndroid Build Coastguard Workerfunc IsEmbedded(structField reflect.StructField) bool {
609*1fa6dee9SAndroid Build Coastguard Worker	return structField.Name == "BlueprintEmbed" || structField.Anonymous
610*1fa6dee9SAndroid Build Coastguard Worker}
611*1fa6dee9SAndroid Build Coastguard Worker
612*1fa6dee9SAndroid Build Coastguard Workertype getStructEmptyError struct{}
613*1fa6dee9SAndroid Build Coastguard Worker
614*1fa6dee9SAndroid Build Coastguard Workerfunc (getStructEmptyError) Error() string { return "interface containing nil pointer" }
615*1fa6dee9SAndroid Build Coastguard Worker
616*1fa6dee9SAndroid Build Coastguard Workerfunc getOrCreateStruct(in interface{}) (reflect.Value, error) {
617*1fa6dee9SAndroid Build Coastguard Worker	value, err := getStruct(in)
618*1fa6dee9SAndroid Build Coastguard Worker	if _, ok := err.(getStructEmptyError); ok {
619*1fa6dee9SAndroid Build Coastguard Worker		value := reflect.ValueOf(in)
620*1fa6dee9SAndroid Build Coastguard Worker		newValue := reflect.New(value.Type().Elem())
621*1fa6dee9SAndroid Build Coastguard Worker		value.Set(newValue)
622*1fa6dee9SAndroid Build Coastguard Worker	}
623*1fa6dee9SAndroid Build Coastguard Worker
624*1fa6dee9SAndroid Build Coastguard Worker	return value, err
625*1fa6dee9SAndroid Build Coastguard Worker}
626*1fa6dee9SAndroid Build Coastguard Worker
627*1fa6dee9SAndroid Build Coastguard Workerfunc getStruct(in interface{}) (reflect.Value, error) {
628*1fa6dee9SAndroid Build Coastguard Worker	value := reflect.ValueOf(in)
629*1fa6dee9SAndroid Build Coastguard Worker	if !isStructPtr(value.Type()) {
630*1fa6dee9SAndroid Build Coastguard Worker		return reflect.Value{}, fmt.Errorf("expected pointer to struct, got %s", value.Type())
631*1fa6dee9SAndroid Build Coastguard Worker	}
632*1fa6dee9SAndroid Build Coastguard Worker	if value.IsNil() {
633*1fa6dee9SAndroid Build Coastguard Worker		return reflect.Value{}, getStructEmptyError{}
634*1fa6dee9SAndroid Build Coastguard Worker	}
635*1fa6dee9SAndroid Build Coastguard Worker	value = value.Elem()
636*1fa6dee9SAndroid Build Coastguard Worker	return value, nil
637*1fa6dee9SAndroid Build Coastguard Worker}
638