github.com/vmware/govmomi@v0.37.1/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 }