github.com/vmware/govmomi@v0.37.2/vim25/mo/type_info.go (about)

     1  /*
     2  Copyright (c) 2014 VMware, Inc. All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package mo
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"regexp"
    23  	"strings"
    24  	"sync"
    25  
    26  	"github.com/vmware/govmomi/vim25/types"
    27  )
    28  
    29  type typeInfo struct {
    30  	typ reflect.Type
    31  
    32  	// Field indices of "Self" field.
    33  	self []int
    34  
    35  	// Map property names to field indices.
    36  	props map[string][]int
    37  }
    38  
    39  var typeInfoLock sync.RWMutex
    40  var typeInfoMap = make(map[string]*typeInfo)
    41  
    42  func typeInfoForType(tname string) *typeInfo {
    43  	typeInfoLock.RLock()
    44  	ti, ok := typeInfoMap[tname]
    45  	typeInfoLock.RUnlock()
    46  
    47  	if ok {
    48  		return ti
    49  	}
    50  
    51  	// Create new typeInfo for type.
    52  	if typ, ok := t[tname]; !ok {
    53  		panic("unknown type: " + tname)
    54  	} else {
    55  		// Multiple routines may race to set it, but the result is the same.
    56  		typeInfoLock.Lock()
    57  		ti = newTypeInfo(typ)
    58  		typeInfoMap[tname] = ti
    59  		typeInfoLock.Unlock()
    60  	}
    61  
    62  	return ti
    63  }
    64  
    65  func newTypeInfo(typ reflect.Type) *typeInfo {
    66  	t := typeInfo{
    67  		typ:   typ,
    68  		props: make(map[string][]int),
    69  	}
    70  
    71  	t.build(typ, "", []int{})
    72  
    73  	return &t
    74  }
    75  
    76  var managedObjectRefType = reflect.TypeOf((*types.ManagedObjectReference)(nil)).Elem()
    77  
    78  func buildName(fn string, f reflect.StructField) string {
    79  	if fn != "" {
    80  		fn += "."
    81  	}
    82  
    83  	motag := f.Tag.Get("json")
    84  	if motag != "" {
    85  		tokens := strings.Split(motag, ",")
    86  		if tokens[0] != "" {
    87  			return fn + tokens[0]
    88  		}
    89  	}
    90  
    91  	xmltag := f.Tag.Get("xml")
    92  	if xmltag != "" {
    93  		tokens := strings.Split(xmltag, ",")
    94  		if tokens[0] != "" {
    95  			return fn + tokens[0]
    96  		}
    97  	}
    98  
    99  	return ""
   100  }
   101  
   102  func (t *typeInfo) build(typ reflect.Type, fn string, fi []int) {
   103  	if typ.Kind() == reflect.Ptr {
   104  		typ = typ.Elem()
   105  	}
   106  
   107  	if typ.Kind() != reflect.Struct {
   108  		panic("need struct")
   109  	}
   110  
   111  	for i := 0; i < typ.NumField(); i++ {
   112  		f := typ.Field(i)
   113  		ftyp := f.Type
   114  
   115  		// Copy field indices so they can be passed along.
   116  		fic := make([]int, len(fi)+1)
   117  		copy(fic, fi)
   118  		fic[len(fi)] = i
   119  
   120  		// Recurse into embedded field.
   121  		if f.Anonymous {
   122  			t.build(ftyp, fn, fic)
   123  			continue
   124  		}
   125  
   126  		// Top level type has a "Self" field.
   127  		if f.Name == "Self" && ftyp == managedObjectRefType {
   128  			t.self = fic
   129  			continue
   130  		}
   131  
   132  		fnc := buildName(fn, f)
   133  		if fnc == "" {
   134  			continue
   135  		}
   136  
   137  		t.props[fnc] = fic
   138  
   139  		// Dereference pointer.
   140  		if ftyp.Kind() == reflect.Ptr {
   141  			ftyp = ftyp.Elem()
   142  		}
   143  
   144  		// Slices are not addressable by `foo.bar.qux`.
   145  		if ftyp.Kind() == reflect.Slice {
   146  			continue
   147  		}
   148  
   149  		// Skip the managed reference type.
   150  		if ftyp == managedObjectRefType {
   151  			continue
   152  		}
   153  
   154  		// Recurse into structs.
   155  		if ftyp.Kind() == reflect.Struct {
   156  			t.build(ftyp, fnc, fic)
   157  		}
   158  	}
   159  }
   160  
   161  var nilValue reflect.Value
   162  
   163  // assignValue assigns a value 'pv' to the struct pointed to by 'val', given a
   164  // slice of field indices. It recurses into the struct until it finds the field
   165  // specified by the indices. It creates new values for pointer types where
   166  // needed.
   167  func assignValue(val reflect.Value, fi []int, pv reflect.Value) {
   168  	// Create new value if necessary.
   169  	if val.Kind() == reflect.Ptr {
   170  		if val.IsNil() {
   171  			val.Set(reflect.New(val.Type().Elem()))
   172  		}
   173  
   174  		val = val.Elem()
   175  	}
   176  
   177  	rv := val.Field(fi[0])
   178  	fi = fi[1:]
   179  	if len(fi) == 0 {
   180  		if pv == nilValue {
   181  			pv = reflect.Zero(rv.Type())
   182  			rv.Set(pv)
   183  			return
   184  		}
   185  		rt := rv.Type()
   186  		pt := pv.Type()
   187  
   188  		// If type is a pointer, create new instance of type.
   189  		if rt.Kind() == reflect.Ptr {
   190  			rv.Set(reflect.New(rt.Elem()))
   191  			rv = rv.Elem()
   192  			rt = rv.Type()
   193  		}
   194  
   195  		// If the target type is a slice, but the source is not, deference any ArrayOfXYZ type
   196  		if rt.Kind() == reflect.Slice && pt.Kind() != reflect.Slice {
   197  			if pt.Kind() == reflect.Ptr {
   198  				pv = pv.Elem()
   199  				pt = pt.Elem()
   200  			}
   201  
   202  			m := arrayOfRegexp.FindStringSubmatch(pt.Name())
   203  			if len(m) > 0 {
   204  				pv = pv.FieldByName(m[1]) // ArrayOfXYZ type has single field named XYZ
   205  				pt = pv.Type()
   206  
   207  				if !pv.IsValid() {
   208  					panic(fmt.Sprintf("expected %s type to have field %s", m[0], m[1]))
   209  				}
   210  			}
   211  		}
   212  
   213  		// If type is an interface, check if pv implements it.
   214  		if rt.Kind() == reflect.Interface && !pt.Implements(rt) {
   215  			// Check if pointer to pv implements it.
   216  			if reflect.PtrTo(pt).Implements(rt) {
   217  				npv := reflect.New(pt)
   218  				npv.Elem().Set(pv)
   219  				pv = npv
   220  				pt = pv.Type()
   221  			} else {
   222  				panic(fmt.Sprintf("type %s doesn't implement %s", pt.Name(), rt.Name()))
   223  			}
   224  		} else if rt.Kind() == reflect.Struct && pt.Kind() == reflect.Ptr {
   225  			pv = pv.Elem()
   226  			pt = pv.Type()
   227  		}
   228  
   229  		if pt.AssignableTo(rt) {
   230  			rv.Set(pv)
   231  		} else if rt.ConvertibleTo(pt) {
   232  			rv.Set(pv.Convert(rt))
   233  		} else {
   234  			panic(fmt.Sprintf("cannot assign %q (%s) to %q (%s)", rt.Name(), rt.Kind(), pt.Name(), pt.Kind()))
   235  		}
   236  
   237  		return
   238  	}
   239  
   240  	assignValue(rv, fi, pv)
   241  }
   242  
   243  var arrayOfRegexp = regexp.MustCompile("ArrayOf(.*)$")
   244  
   245  // LoadObjectFromContent loads properties from the 'PropSet' field in the
   246  // specified ObjectContent value into the value it represents, which is
   247  // returned as a reflect.Value.
   248  func (t *typeInfo) LoadFromObjectContent(o types.ObjectContent) (reflect.Value, error) {
   249  	v := reflect.New(t.typ)
   250  	assignValue(v, t.self, reflect.ValueOf(o.Obj))
   251  
   252  	for _, p := range o.PropSet {
   253  		rv, ok := t.props[p.Name]
   254  		if !ok {
   255  			continue
   256  		}
   257  		assignValue(v, rv, reflect.ValueOf(p.Val))
   258  	}
   259  
   260  	return v, nil
   261  }
   262  
   263  func IsManagedObjectType(kind string) bool {
   264  	_, ok := t[kind]
   265  	return ok
   266  }