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  }