1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
package toolbox
import (
"fmt"
"reflect"
)
//Context represents type safe map.
type Context interface {
//GetRequired returns a value for a target type of error if it does not exist
GetRequired(targetType interface{}) (interface{}, error)
//GetOptional returns a value for a target type
GetOptional(targetType interface{}) interface{}
//GetOptional into sets requested context value into target, returns true if value was found
GetInto(targetType interface{}, target interface{}) bool
//Put puts target type value to the context, or error if value exists, is nil or incompatible with target type
Put(targetType interface{}, value interface{}) error
//Replace repaces value in the context
Replace(targetType interface{}, value interface{}) error
//Remove removes value from the context
Remove(targetType interface{}) interface{}
//Contains chekcs if a value of a terget type is in contet
Contains(targetType interface{}) bool
//Clone create a shallow copy of a context
Clone() Context
}
type contextImpl struct {
context map[string]interface{}
}
func (c *contextImpl) getReflectType(targetType interface{}) reflect.Type {
var reflectType reflect.Type
var ok bool
reflectType, ok = targetType.(reflect.Type)
if !ok {
reflectType = reflect.TypeOf(targetType)
}
return reflectType
}
func (c *contextImpl) getKey(targetType interface{}) string {
var reflectType = c.getReflectType(targetType)
return reflectType.String()
}
func (c *contextImpl) GetRequired(targetType interface{}) (interface{}, error) {
if !c.Contains(targetType) {
key := c.getKey(targetType)
return nil, fmt.Errorf("failed to lookup key:" + key)
}
return c.GetOptional(targetType), nil
}
func (c *contextImpl) GetOptional(targetType interface{}) interface{} {
key := c.getKey(targetType)
if result, ok := c.context[key]; ok {
return result
}
return nil
}
func (c *contextImpl) GetInto(targetType, target interface{}) bool {
key := c.getKey(targetType)
if result, ok := c.context[key]; ok {
reflect.ValueOf(target).Elem().Set(reflect.ValueOf(result))
return true
}
return false
}
func (c *contextImpl) Put(targetType interface{}, value interface{}) error {
if c.Contains(targetType) {
key := c.getKey(targetType)
return fmt.Errorf("failed to put key - already exist: " + key)
}
return c.Replace(targetType, value)
}
func (c *contextImpl) Replace(targetType interface{}, value interface{}) error {
key := c.getKey(targetType)
targetReflectType := c.getReflectType(targetType)
valueReflectType := reflect.TypeOf(value)
if valueReflectType == targetReflectType {
c.context[key] = value
return nil
}
if targetReflectType.Kind() == reflect.Ptr {
converted := reflect.ValueOf(value).Elem().Convert(targetReflectType.Elem())
convertedPointer := reflect.New(targetReflectType.Elem())
convertedPointer.Elem().Set(converted)
value = convertedPointer.Interface()
} else {
if !valueReflectType.AssignableTo(targetReflectType) {
return fmt.Errorf("value of type %v is not assignable to %v", valueReflectType, targetReflectType)
}
value = reflect.ValueOf(value).Convert(targetReflectType).Interface()
}
c.context[key] = value
return nil
}
func (c *contextImpl) Remove(targetType interface{}) interface{} {
key := c.getKey(targetType)
result := c.GetOptional(targetType)
delete(c.context, key)
return result
}
func (c *contextImpl) Contains(targetType interface{}) bool {
key := c.getKey(targetType)
if _, ok := c.context[key]; ok {
return true
}
return false
}
func (c *contextImpl) Clone() Context {
var result = &contextImpl{context: make(map[string]interface{})}
for k, v := range c.context {
result.context[k] = v
}
return result
}
//NewContext creates a new context
func NewContext() Context {
var result Context = &contextImpl{context: make(map[string]interface{})}
return result
}
|