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  }