github.com/goki/ki@v1.1.11/kit/types.go (about) 1 // Copyright (c) 2018, The GoKi Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package kit provides various reflect type functions for GoKi system, including: 6 // 7 // * kit.TypeRegistry (types.go) for associating string names with 8 // reflect.Type values, to allow dynamic marshaling of structs, and also 9 // bidirectional string conversion of const int iota (enum) types. It is used 10 // by the GoKi ki system, hence the kit (ki types) name. 11 // 12 // To register a new type, add: 13 // 14 // var KiT_TypeName = kit.Types.AddType(&TypeName{}, [props|nil]) 15 // 16 // where the props is a map[string]interface{} of optional properties that can 17 // be associated with the type -- this is used in the GoGi graphical interface 18 // system for example to color objects of different types using the 19 // background-color property. KiT_TypeName variable can be conveniently used 20 // wherever a reflect.Type of that type is needed. 21 // 22 // * kit.EnumRegistry (enums.go) that registers constant int iota (aka enum) types, and 23 // provides general conversion utilities to / from string, int64, general 24 // properties associated with enum types, and deals with bit flags 25 // 26 // * kit.Type (type.go) struct provides JSON and XML Marshal / Unmarshal functions for 27 // saving / loading reflect.Type using registrered type names. 28 // 29 // * convert.go: robust interface{}-based type conversion routines that are 30 // useful in more lax user-interface contexts where "common sense" conversions 31 // between strings, numbers etc are useful 32 // 33 // * embeds.go: various functions for managing embedded struct types, e.g., 34 // determining if a given type embeds another type (directly or indirectly), 35 // and iterating over fields to flatten the otherwise nested nature of the 36 // field encoding in embedded types. 37 package kit 38 39 import ( 40 "fmt" 41 "log" 42 "path" 43 "reflect" 44 "strings" 45 "sync" 46 ) 47 48 // TypeRegistry contains several maps where properties of types 49 // are maintained for general usage. 50 // 51 // See also EnumRegistry for a version specifically for "enum" types 52 // (const int types that associate a name with an int value). 53 // 54 // Each type must be explicitly registered by calling AddType 55 // in an expression that also initializes a new global variable 56 // that is then useful whenever you need to specify that type, 57 // e.g., when adding a new Node in the Ki system. 58 // 59 // var KiT_MyType = ki.Types.AddType(&MyType{}, [props|nil]) 60 // 61 // where MyType is the type -- note that it is ESSENTIAL to pass a pointer 62 // so that the type is considered addressable, even after we get Elem() of it. 63 // 64 // props is a map[string]interface{} of optional properties that can be 65 // associated with the type -- this is used in the GoGi graphical interface 66 // system for example to configure menus and toolbars for different types. 67 // 68 // Once registered, the reflect.Type can be looked up via the usual 69 // *short* package-qualified type name that you use in programming 70 // (e.g., kit.TypeRegistry) -- this is maintained in the Types map. 71 // This lookup enables e.g., saving type name information in JSON files 72 // and then using that type name to create an object of that type 73 // upon loading. 74 // 75 // The Props map stores the properties (which can be updated during 76 // runtime as well), and Insts also stores an interface{} pointer to 77 // each type that has been registered. 78 type TypeRegistry struct { 79 // Types is a map from the *short* package qualified name to reflect.Type 80 Types map[string]reflect.Type 81 82 // ShortNames is a map of short package qualified names keyed by 83 // the long, fully unambiguous package path qualified name. 84 // It is somewhat expensive to compute this short name, so 85 // caching it is faster 86 ShortNames map[string]string 87 88 // Props are type properties -- nodes can get default properties from 89 // their types and then optionally override them with their own settings. 90 // The key here is the long, full type name. 91 Props map[string]map[string]interface{} 92 93 // Insts contain an instance of each type (the one passed during AddType) 94 // The key here is the long, full type name. 95 Insts map[string]interface{} 96 } 97 98 // Types is master registry of types that embed Ki Nodes 99 var Types TypeRegistry 100 101 // LongTypeName returns the long, full package-path qualified type name. 102 // This is guaranteed to be unique and used for internal storage of 103 // several maps to avoid any conflicts. It is also very quick to compute. 104 func LongTypeName(typ reflect.Type) string { 105 return typ.PkgPath() + "." + typ.Name() 106 } 107 108 // ShortTypeName returns the short version of a package-qualified type name 109 // which just has the last element of the path. This is what is used in 110 // standard Go programming, and is is used for the key to lookup reflect.Type 111 // names -- i.e., this is what you should save in a JSON file. 112 // The potential naming conflict is worth the brevity, and typically a given 113 // file will only contain mutually-compatible, non-conflicting types. 114 // This is cached in ShortNames because the path.Base computation is apparently 115 // a bit slow. 116 func ShortTypeName(typ reflect.Type) string { 117 return path.Base(typ.PkgPath()) + "." + typ.Name() 118 } 119 120 // TypesMu protects updating of the type registry maps -- main Addtype etc all 121 // happens at startup and does not need protection, but property access does. 122 // use RLock for read-access to properties, and Lock for write access when 123 // adding or changing key / value. 124 var TypesMu sync.RWMutex 125 126 // AddType adds a given type to the registry -- requires an empty object to 127 // grab type info from (which is then stored in Insts) -- must be passed as a 128 // pointer to ensure that it is an addressable, settable type -- also optional 129 // properties that can be associated with the type and accessible e.g. for 130 // view-specific properties etc -- these props MUST be specific to this type 131 // as they are used directly, not copied!! 132 func (tr *TypeRegistry) AddType(obj interface{}, props map[string]interface{}) reflect.Type { 133 if tr.Types == nil { 134 tr.Init() 135 } 136 137 typ := reflect.TypeOf(obj).Elem() 138 lnm := LongTypeName(typ) 139 snm := ShortTypeName(typ) 140 tr.ShortNames[lnm] = snm 141 tr.Types[snm] = typ 142 tr.Insts[lnm] = obj 143 if props != nil { 144 // make a copy of props for enums -- often shared 145 nwprops := make(map[string]interface{}, len(props)) 146 for key, val := range props { 147 nwprops[key] = val 148 } 149 tr.Props[lnm] = nwprops 150 } 151 // tr.InheritTypeProps(typ) // not actually that useful due to order dependencies. 152 return typ 153 } 154 155 // InheritTypeProps attempts to inherit certain heritable type properties 156 // from first embedded type. Returns true if did. 157 func (tr *TypeRegistry) InheritTypeProps(typ reflect.Type) bool { 158 if typ.Kind() != reflect.Struct || typ.NumField() == 0 { 159 return false 160 } 161 embfld := typ.Field(0) // check first embedded field 162 if !embfld.Anonymous { 163 return false 164 } 165 pp := tr.Properties(embfld.Type, false) 166 if pp == nil { 167 return false 168 } 169 var myp *map[string]interface{} 170 for k, v := range *pp { 171 if strings.HasPrefix(k, "EnumType:") { 172 if myp == nil { 173 myp = tr.Properties(typ, true) 174 } 175 SetTypeProp(*myp, k, v) 176 fmt.Printf("typ: %v inh: %v\n", ShortTypeName(typ), k) 177 } 178 } 179 return myp != nil 180 } 181 182 // TypeName returns the *short* package-qualified type name for given reflect.Type. 183 // This is the version that should be used in saving the type to a file, etc. 184 // It uses a map for fast results. 185 func (tr *TypeRegistry) TypeName(typ reflect.Type) string { 186 lnm := LongTypeName(typ) 187 if snm, ok := tr.ShortNames[lnm]; ok { 188 return snm 189 } 190 snm := ShortTypeName(typ) 191 TypesMu.Lock() 192 tr.ShortNames[lnm] = snm 193 TypesMu.Unlock() 194 return snm 195 } 196 197 // Type returns the reflect.Type based on its *short* package-qualified name 198 // (package directory + "." + type -- the version that you use in programming). 199 // Returns nil if not registered. 200 func (tr *TypeRegistry) Type(typeName string) reflect.Type { 201 if typ, ok := tr.Types[typeName]; ok { 202 return typ 203 } 204 return nil 205 } 206 207 // InstByName returns the interface{} instance of given type (it is a pointer 208 // to that type) using the long, unambiguous package-qualified name. 209 // Returns nil if not found. 210 func (tr *TypeRegistry) InstByName(typeName string) interface{} { 211 if inst, ok := tr.Insts[typeName]; ok { 212 return inst 213 } 214 return nil 215 } 216 217 // Inst returns the interface{} instance of given type (it is a pointer 218 // to that type). Returns nil if not found. 219 func (tr *TypeRegistry) Inst(typ reflect.Type) interface{} { 220 return tr.InstByName(LongTypeName(typ)) 221 } 222 223 // PropsByName returns properties for given type name, using the long, 224 // unambiguous package-qualified name. 225 // It optionally makes props map for this type if not already made. 226 // Can use this to register properties for types that are not registered. 227 func (tr *TypeRegistry) PropsByName(typeName string, makeNew bool) *map[string]interface{} { 228 TypesMu.Lock() 229 defer TypesMu.Unlock() 230 tp, ok := tr.Props[typeName] 231 if !ok { 232 if !makeNew { 233 return nil 234 } 235 tp = make(map[string]interface{}) 236 tr.Props[typeName] = tp 237 } 238 return &tp 239 } 240 241 // Properties returns properties for given type. 242 // It optionally makes props map for this type if not already made. 243 // Can use this to register properties for types that are not registered. 244 func (tr *TypeRegistry) Properties(typ reflect.Type, makeNew bool) *map[string]interface{} { 245 return tr.PropsByName(LongTypeName(typ), makeNew) 246 } 247 248 // TypeProp provides safe (mutex protected) read access to property map 249 // returned by Properties method -- must use this for all Properties access! 250 func TypeProp(props map[string]interface{}, key string) (interface{}, bool) { 251 TypesMu.RLock() 252 val, ok := props[key] 253 TypesMu.RUnlock() 254 return val, ok 255 } 256 257 // SetTypeProp provides safe (mutex protected) write setting of property map 258 // returned by Properties method -- must use this for all Properties access! 259 func SetTypeProp(props map[string]interface{}, key string, val interface{}) { 260 TypesMu.Lock() 261 props[key] = val 262 TypesMu.Unlock() 263 } 264 265 // PropByName safely finds a type property from type name (using the long, 266 // unambiguous package-qualified name) and property key. 267 // Returns false if not found 268 func (tr *TypeRegistry) PropByName(typeName, propKey string) (interface{}, bool) { 269 TypesMu.RLock() 270 defer TypesMu.RUnlock() 271 272 tp, ok := tr.Props[typeName] 273 if !ok { 274 // fmt.Printf("no props for type: %v\n", typeName) 275 return nil, false 276 } 277 p, ok := tp[propKey] 278 return p, ok 279 } 280 281 // Prop safely finds a type property from type and property key -- returns 282 // false if not found. 283 func (tr *TypeRegistry) Prop(typ reflect.Type, propKey string) (interface{}, bool) { 284 return tr.PropByName(LongTypeName(typ), propKey) 285 } 286 287 // SetProps sets the type props for given type, uses write mutex lock 288 func (tr *TypeRegistry) SetProps(typ reflect.Type, props map[string]interface{}) { 289 TypesMu.Lock() 290 defer TypesMu.Unlock() 291 tr.Props[LongTypeName(typ)] = props 292 } 293 294 // AllImplementersOf returns a list of all registered types that implement the 295 // given interface type at any level of embedding -- must pass a type 296 // constructed like this: reflect.TypeOf((*gi.Node2D)(nil)).Elem() -- 297 // includeBases indicates whether to include types marked with property of 298 // base-type -- typically not useful for user-facing type selection 299 func (tr *TypeRegistry) AllImplementersOf(iface reflect.Type, includeBases bool) []reflect.Type { 300 if iface.Kind() != reflect.Interface { 301 log.Printf("kit.TypeRegistry AllImplementersOf -- type is not an interface: %v\n", iface) 302 return nil 303 } 304 tl := make([]reflect.Type, 0) 305 for _, typ := range tr.Types { 306 if !includeBases { 307 if btp, ok := tr.Prop(typ, "base-type"); ok { 308 if bt, ok := ToBool(btp); ok && bt { 309 continue 310 } 311 } 312 } 313 nptyp := NonPtrType(typ) 314 if nptyp.Kind() != reflect.Struct { 315 continue 316 } 317 if EmbedImplements(typ, iface) { 318 tl = append(tl, typ) 319 } 320 } 321 return tl 322 } 323 324 // AllEmbedsOf returns a list of all registered types that embed (inherit from 325 // in C++ terminology) the given type -- inclusive determines whether the type 326 // itself is included in list -- includeBases indicates whether to include 327 // types marked with property of base-type -- typically not useful for 328 // user-facing type selection 329 func (tr *TypeRegistry) AllEmbedsOf(embed reflect.Type, inclusive, includeBases bool) []reflect.Type { 330 tl := make([]reflect.Type, 0) 331 for _, typ := range tr.Types { 332 if !inclusive && typ == embed { 333 continue 334 } 335 if !includeBases { 336 if btp, ok := tr.Prop(typ, "base-type"); ok { 337 if bt, ok := ToBool(btp); ok && bt { 338 continue 339 } 340 } 341 } 342 if TypeEmbeds(typ, embed) { 343 tl = append(tl, typ) 344 } 345 } 346 return tl 347 } 348 349 // AllTagged returns a list of all registered types that include a given 350 // property key value -- does not check for the value of that value -- just 351 // its existence 352 func (tr *TypeRegistry) AllTagged(key string) []reflect.Type { 353 tl := make([]reflect.Type, 0) 354 for _, typ := range tr.Types { 355 _, ok := tr.Prop(typ, key) 356 if !ok { 357 continue 358 } 359 tl = append(tl, typ) 360 } 361 return tl 362 } 363 364 // Init initializes the type registry, including adding basic types 365 func (tr *TypeRegistry) Init() { 366 tr.Types = make(map[string]reflect.Type, 1000) 367 tr.Insts = make(map[string]interface{}, 1000) 368 tr.Props = make(map[string]map[string]interface{}, 1000) 369 tr.ShortNames = make(map[string]string, 1000) 370 371 { 372 var BoolProps = map[string]interface{}{ 373 "basic-type": true, 374 } 375 ob := false 376 tr.AddType(&ob, BoolProps) 377 } 378 { 379 var IntProps = map[string]interface{}{ 380 "basic-type": true, 381 } 382 ob := int(0) 383 tr.AddType(&ob, IntProps) 384 } 385 { 386 ob := int8(0) 387 tr.AddType(&ob, nil) 388 } 389 { 390 ob := int16(0) 391 tr.AddType(&ob, nil) 392 } 393 { 394 ob := int32(0) 395 tr.AddType(&ob, nil) 396 } 397 { 398 ob := int64(0) 399 tr.AddType(&ob, nil) 400 } 401 { 402 ob := uint(0) 403 tr.AddType(&ob, nil) 404 } 405 { 406 ob := uint8(0) 407 tr.AddType(&ob, nil) 408 } 409 { 410 ob := uint16(0) 411 tr.AddType(&ob, nil) 412 } 413 { 414 ob := uint32(0) 415 tr.AddType(&ob, nil) 416 } 417 { 418 ob := uint64(0) 419 tr.AddType(&ob, nil) 420 } 421 { 422 ob := uintptr(0) 423 tr.AddType(&ob, nil) 424 } 425 { 426 ob := float32(0) 427 tr.AddType(&ob, nil) 428 } 429 { 430 var Float64Props = map[string]interface{}{ 431 "basic-type": true, 432 } 433 ob := float64(0) 434 tr.AddType(&ob, Float64Props) 435 } 436 { 437 ob := complex64(0) 438 tr.AddType(&ob, nil) 439 } 440 { 441 ob := complex128(0) 442 tr.AddType(&ob, nil) 443 } 444 { 445 var StringProps = map[string]interface{}{ 446 "basic-type": true, 447 } 448 ob := "" 449 tr.AddType(&ob, StringProps) 450 } 451 }