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