github.com/vmware/govmomi@v0.51.0/vim25/mo/type_info.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package mo 6 7 import ( 8 "fmt" 9 "reflect" 10 "regexp" 11 "strconv" 12 "strings" 13 "sync" 14 15 "github.com/vmware/govmomi/vim25/types" 16 ) 17 18 type typeInfo struct { 19 typ reflect.Type 20 21 // Field indices of "Self" field. 22 self []int 23 24 // Map property names to field indices. 25 props map[string][]int 26 } 27 28 var typeInfoLock sync.RWMutex 29 var typeInfoMap = make(map[string]*typeInfo) 30 31 func typeInfoForType(tname string) *typeInfo { 32 typeInfoLock.RLock() 33 ti, ok := typeInfoMap[tname] 34 typeInfoLock.RUnlock() 35 36 if ok { 37 return ti 38 } 39 40 // Create new typeInfo for type. 41 if typ, ok := t[tname]; !ok { 42 panic("unknown type: " + tname) 43 } else { 44 // Multiple routines may race to set it, but the result is the same. 45 typeInfoLock.Lock() 46 ti = newTypeInfo(typ) 47 typeInfoMap[tname] = ti 48 typeInfoLock.Unlock() 49 } 50 51 return ti 52 } 53 54 func baseType(ftyp reflect.Type) reflect.Type { 55 base := strings.TrimPrefix(ftyp.Name(), "Base") 56 switch base { 57 case "MethodFault": 58 return nil 59 } 60 if kind, ok := types.TypeFunc()(base); ok { 61 return kind 62 } 63 return nil 64 } 65 66 func newTypeInfo(typ reflect.Type) *typeInfo { 67 t := typeInfo{ 68 typ: typ, 69 props: make(map[string][]int), 70 } 71 72 t.build(typ, "", []int{}) 73 74 return &t 75 } 76 77 var managedObjectRefType = reflect.TypeOf((*types.ManagedObjectReference)(nil)).Elem() 78 79 func buildName(fn string, f reflect.StructField) string { 80 if fn != "" { 81 fn += "." 82 } 83 84 motag := f.Tag.Get("json") 85 if motag != "" { 86 tokens := strings.Split(motag, ",") 87 if tokens[0] != "" { 88 return fn + tokens[0] 89 } 90 } 91 92 xmltag := f.Tag.Get("xml") 93 if xmltag != "" { 94 tokens := strings.Split(xmltag, ",") 95 if tokens[0] != "" { 96 return fn + tokens[0] 97 } 98 } 99 100 return "" 101 } 102 103 func (t *typeInfo) build(typ reflect.Type, fn string, fi []int) { 104 if typ.Kind() == reflect.Ptr { 105 typ = typ.Elem() 106 } 107 108 if typ.Kind() != reflect.Struct { 109 panic("need struct") 110 } 111 112 for i := 0; i < typ.NumField(); i++ { 113 f := typ.Field(i) 114 ftyp := f.Type 115 116 // Copy field indices so they can be passed along. 117 fic := make([]int, len(fi)+1) 118 copy(fic, fi) 119 fic[len(fi)] = i 120 121 // Recurse into embedded field. 122 if f.Anonymous { 123 t.build(ftyp, fn, fic) 124 continue 125 } 126 127 // Top level type has a "Self" field. 128 if f.Name == "Self" && ftyp == managedObjectRefType { 129 t.self = fic 130 continue 131 } 132 133 fnc := buildName(fn, f) 134 if fnc == "" { 135 continue 136 } 137 138 t.props[fnc] = fic 139 140 // Dereference pointer. 141 if ftyp.Kind() == reflect.Ptr { 142 ftyp = ftyp.Elem() 143 } 144 145 // Slices are not addressable by `foo.bar.qux`. 146 if ftyp.Kind() == reflect.Slice { 147 continue 148 } 149 150 // Skip the managed reference type. 151 if ftyp == managedObjectRefType { 152 continue 153 } 154 155 // Recurse into structs. 156 if ftyp.Kind() == reflect.Struct { 157 t.build(ftyp, fnc, fic) 158 } 159 160 // Base type can only access base fields, for example Datastore.Info 161 // is types.BaseDataStore, so we create a new(types.DatastoreInfo) 162 // Indexed property path may traverse into array element fields. 163 // When interface, use the base type to index fields. 164 // For example, BaseVirtualDevice: 165 // config.hardware.device[4000].deviceInfo.label 166 if ftyp.Kind() == reflect.Interface { 167 if base := baseType(ftyp); base != nil { 168 t.build(base, fnc, fic) 169 } 170 } 171 } 172 } 173 174 var nilValue reflect.Value 175 176 // assignValue assigns a value 'pv' to the struct pointed to by 'val', given a 177 // slice of field indices. It recurses into the struct until it finds the field 178 // specified by the indices. It creates new values for pointer types where 179 // needed. 180 func assignValue(val reflect.Value, fi []int, pv reflect.Value, field ...string) { 181 // Indexed property path can only use base types 182 if val.Kind() == reflect.Interface { 183 if val.IsNil() { 184 base := baseType(val.Type()) 185 val.Set(reflect.New(base)) 186 } 187 val = val.Elem() 188 } 189 190 // Create new value if necessary. 191 if val.Kind() == reflect.Ptr { 192 if val.IsNil() { 193 val.Set(reflect.New(val.Type().Elem())) 194 } 195 196 val = val.Elem() 197 } 198 199 rv := val.Field(fi[0]) 200 fi = fi[1:] 201 if len(fi) == 0 { 202 if pv == nilValue { 203 pv = reflect.Zero(rv.Type()) 204 rv.Set(pv) 205 return 206 } 207 rt := rv.Type() 208 pt := pv.Type() 209 210 // If type is a pointer, create new instance of type. 211 if rt.Kind() == reflect.Ptr { 212 rv.Set(reflect.New(rt.Elem())) 213 rv = rv.Elem() 214 rt = rv.Type() 215 } 216 217 // If the target type is a slice, but the source is not, deference any ArrayOfXYZ type 218 if rt.Kind() == reflect.Slice && pt.Kind() != reflect.Slice { 219 if pt.Kind() == reflect.Ptr { 220 pv = pv.Elem() 221 pt = pt.Elem() 222 } 223 224 m := arrayOfRegexp.FindStringSubmatch(pt.Name()) 225 if len(m) > 0 { 226 pv = pv.FieldByName(m[1]) // ArrayOfXYZ type has single field named XYZ 227 pt = pv.Type() 228 229 if !pv.IsValid() { 230 panic(fmt.Sprintf("expected %s type to have field %s", m[0], m[1])) 231 } 232 } 233 } 234 235 // If type is an interface, check if pv implements it. 236 if rt.Kind() == reflect.Interface && !pt.Implements(rt) { 237 // Check if pointer to pv implements it. 238 if reflect.PtrTo(pt).Implements(rt) { 239 npv := reflect.New(pt) 240 npv.Elem().Set(pv) 241 pv = npv 242 pt = pv.Type() 243 } else { 244 panic(fmt.Sprintf("type %s doesn't implement %s", pt.Name(), rt.Name())) 245 } 246 } else if rt.Kind() == reflect.Struct && pt.Kind() == reflect.Ptr { 247 pv = pv.Elem() 248 pt = pv.Type() 249 } 250 251 if pt.AssignableTo(rt) { 252 rv.Set(pv) 253 } else if rt.ConvertibleTo(pt) { 254 rv.Set(pv.Convert(rt)) 255 } else if rt.Kind() == reflect.Slice { 256 // Indexed array value 257 path := field[0] 258 isInterface := rt.Elem().Kind() == reflect.Interface 259 260 if len(path) == 0 { 261 // Append item (pv) directly to the array, converting to pointer if interface 262 if isInterface { 263 npv := reflect.New(pt) 264 npv.Elem().Set(pv) 265 pv = npv 266 pt = pv.Type() 267 } 268 } else { 269 // Construct item to be appended to the array, setting field within to value of pv 270 var item reflect.Value 271 if isInterface { 272 base := baseType(rt.Elem()) 273 item = reflect.New(base) 274 } else { 275 item = reflect.New(rt.Elem()) 276 } 277 278 field := newTypeInfo(item.Type()) 279 if ix, ok := field.props[path]; ok { 280 assignValue(item, ix, pv) 281 } 282 283 if rt.Elem().Kind() == reflect.Struct { 284 pv = item.Elem() 285 } else { 286 pv = item 287 } 288 pt = pv.Type() 289 } 290 291 rv.Set(reflect.Append(rv, pv)) 292 } else { 293 panic(fmt.Sprintf("cannot assign %q (%s) to %q (%s)", rt.Name(), rt.Kind(), pt.Name(), pt.Kind())) 294 } 295 296 return 297 } 298 299 assignValue(rv, fi, pv, field...) 300 } 301 302 var arrayOfRegexp = regexp.MustCompile("ArrayOf(.*)$") 303 304 // LoadObjectFromContent loads properties from the 'PropSet' field in the 305 // specified ObjectContent value into the value it represents, which is 306 // returned as a reflect.Value. 307 func (t *typeInfo) LoadFromObjectContent(o types.ObjectContent) (reflect.Value, error) { 308 v := reflect.New(t.typ) 309 assignValue(v, t.self, reflect.ValueOf(o.Obj)) 310 311 for _, p := range o.PropSet { 312 var field Field 313 field.FromString(p.Name) 314 315 rv, ok := t.props[field.Path] 316 if !ok { 317 continue 318 } 319 assignValue(v, rv, reflect.ValueOf(p.Val), field.Item) 320 } 321 322 return v, nil 323 } 324 325 func IsManagedObjectType(kind string) bool { 326 _, ok := t[kind] 327 return ok 328 } 329 330 // Value returns a new mo instance of the given ref Type. 331 func Value(ref types.ManagedObjectReference) (Reference, bool) { 332 if rt, ok := t[ref.Type]; ok { 333 val := reflect.New(rt) 334 if e, ok := val.Interface().(Entity); ok { 335 e.Entity().Self = ref 336 return val.Elem().Interface().(Reference), true 337 } 338 } 339 return nil, false 340 } 341 342 // Field of a ManagedObject in string form. 343 type Field struct { 344 Path string 345 Key any 346 Item string 347 } 348 349 func (f *Field) String() string { 350 if f.Key == nil { 351 return f.Path 352 } 353 354 var key, item string 355 356 switch f.Key.(type) { 357 case string: 358 key = fmt.Sprintf("%q", f.Key) 359 default: 360 key = fmt.Sprintf("%d", f.Key) 361 } 362 363 if f.Item != "" { 364 item = "." + f.Item 365 } 366 367 return fmt.Sprintf("%s[%s]%s", f.Path, key, item) 368 } 369 370 func (f *Field) FromString(spec string) bool { 371 s := strings.SplitN(spec, "[", 2) 372 f.Path = s[0] 373 f.Key = nil 374 f.Item = "" 375 if len(s) == 1 { 376 return true 377 } 378 379 parts := strings.SplitN(s[1], "]", 2) 380 381 if len(parts) != 2 { 382 return false 383 } 384 385 ix := strings.Trim(parts[0], `"`) 386 387 if ix == parts[0] { 388 v, err := strconv.ParseInt(ix, 0, 32) 389 if err != nil { 390 return false 391 } 392 f.Key = int32(v) 393 } else { 394 f.Key = ix 395 } 396 397 if parts[1] == "" { 398 return true 399 } 400 401 if parts[1][0] != '.' { 402 return false 403 } 404 f.Item = parts[1][1:] 405 406 return true 407 }