github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/object.go (about)

     1  package values
     2  
     3  import (
     4  	"context"
     5  	"encoding/binary"
     6  	"hash/fnv"
     7  	"sort"
     8  
     9  	"github.com/wI2L/jettison"
    10  
    11  	"github.com/MontFerret/ferret/pkg/runtime/core"
    12  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    13  )
    14  
    15  type (
    16  	ObjectPredicate = func(value core.Value, key string) bool
    17  
    18  	ObjectProperty struct {
    19  		key   string
    20  		value core.Value
    21  	}
    22  
    23  	Object struct {
    24  		value map[string]core.Value
    25  	}
    26  )
    27  
    28  func NewObjectProperty(name string, value core.Value) *ObjectProperty {
    29  	return &ObjectProperty{name, value}
    30  }
    31  
    32  func NewObject() *Object {
    33  	return &Object{make(map[string]core.Value)}
    34  }
    35  
    36  func NewObjectWith(props ...*ObjectProperty) *Object {
    37  	obj := NewObject()
    38  
    39  	for _, prop := range props {
    40  		obj.value[prop.key] = prop.value
    41  	}
    42  
    43  	return obj
    44  }
    45  
    46  func (t *Object) MarshalJSON() ([]byte, error) {
    47  	return jettison.MarshalOpts(t.value, jettison.NoHTMLEscaping())
    48  }
    49  
    50  func (t *Object) Type() core.Type {
    51  	return types.Object
    52  }
    53  
    54  func (t *Object) String() string {
    55  	marshaled, err := t.MarshalJSON()
    56  
    57  	if err != nil {
    58  		return "{}"
    59  	}
    60  
    61  	return string(marshaled)
    62  }
    63  
    64  // Compare compares the source object with other core.Value
    65  // The behavior of the Compare is similar
    66  // to the comparison of objects in ArangoDB
    67  func (t *Object) Compare(other core.Value) int64 {
    68  	if other.Type() == t.Type() {
    69  		other := other.(*Object)
    70  
    71  		if t.Length() == 0 && other.Length() == 0 {
    72  			return 0
    73  		}
    74  
    75  		if t.Length() < other.Length() {
    76  			return -1
    77  		}
    78  
    79  		if t.Length() > other.Length() {
    80  			return 1
    81  		}
    82  
    83  		var res int64
    84  
    85  		tKeys := make([]string, 0, len(t.value))
    86  
    87  		for k := range t.value {
    88  			tKeys = append(tKeys, k)
    89  		}
    90  
    91  		sortedT := sort.StringSlice(tKeys)
    92  		sortedT.Sort()
    93  
    94  		otherKeys := make([]string, 0, other.Length())
    95  
    96  		other.ForEach(func(value core.Value, k string) bool {
    97  			otherKeys = append(otherKeys, k)
    98  			return true
    99  		})
   100  
   101  		sortedOther := sort.StringSlice(otherKeys)
   102  		sortedOther.Sort()
   103  
   104  		var tVal, otherVal core.Value
   105  		var tKey, otherKey string
   106  
   107  		for i := 0; i < len(t.value) && res == 0; i++ {
   108  			tKey, otherKey = sortedT[i], sortedOther[i]
   109  
   110  			if tKey == otherKey {
   111  				tVal, _ = t.Get(NewString(tKey))
   112  				otherVal, _ = other.Get(NewString(tKey))
   113  				res = tVal.Compare(otherVal)
   114  
   115  				continue
   116  			}
   117  
   118  			if tKey < otherKey {
   119  				res = 1
   120  			} else {
   121  				res = -1
   122  			}
   123  
   124  			break
   125  		}
   126  
   127  		return res
   128  	}
   129  
   130  	return types.Compare(types.Object, other.Type())
   131  }
   132  
   133  func (t *Object) Unwrap() interface{} {
   134  	obj := make(map[string]interface{})
   135  
   136  	for key, val := range t.value {
   137  		obj[key] = val.Unwrap()
   138  	}
   139  
   140  	return obj
   141  }
   142  
   143  func (t *Object) Hash() uint64 {
   144  	h := fnv.New64a()
   145  
   146  	h.Write([]byte(t.Type().String()))
   147  	h.Write([]byte(":"))
   148  	h.Write([]byte("{"))
   149  
   150  	keys := make([]string, 0, len(t.value))
   151  
   152  	for key := range t.value {
   153  		keys = append(keys, key)
   154  	}
   155  
   156  	// order does not really matter
   157  	// but it will give us a consistent hash sum
   158  	sort.Strings(keys)
   159  	endIndex := len(keys) - 1
   160  
   161  	for idx, key := range keys {
   162  		h.Write([]byte(key))
   163  		h.Write([]byte(":"))
   164  
   165  		el := t.value[key]
   166  
   167  		bytes := make([]byte, 8)
   168  		binary.LittleEndian.PutUint64(bytes, el.Hash())
   169  
   170  		h.Write(bytes)
   171  
   172  		if idx != endIndex {
   173  			h.Write([]byte(","))
   174  		}
   175  	}
   176  
   177  	h.Write([]byte("}"))
   178  
   179  	return h.Sum64()
   180  }
   181  
   182  func (t *Object) Copy() core.Value {
   183  	c := NewObject()
   184  
   185  	for k, v := range t.value {
   186  		c.Set(NewString(k), v)
   187  	}
   188  
   189  	return c
   190  }
   191  
   192  func (t *Object) Length() Int {
   193  	return Int(len(t.value))
   194  }
   195  
   196  func (t *Object) Keys() []String {
   197  	keys := make([]String, 0, len(t.value))
   198  
   199  	for k := range t.value {
   200  		keys = append(keys, NewString(k))
   201  	}
   202  
   203  	return keys
   204  }
   205  
   206  func (t *Object) Values() []core.Value {
   207  	keys := make([]core.Value, 0, len(t.value))
   208  
   209  	for _, v := range t.value {
   210  		keys = append(keys, v)
   211  	}
   212  
   213  	return keys
   214  }
   215  
   216  func (t *Object) ForEach(predicate ObjectPredicate) {
   217  	for key, val := range t.value {
   218  		if !predicate(val, key) {
   219  			break
   220  		}
   221  	}
   222  }
   223  
   224  func (t *Object) Find(predicate ObjectPredicate) (core.Value, Boolean) {
   225  	for idx, val := range t.value {
   226  		if predicate(val, idx) {
   227  			return val, True
   228  		}
   229  	}
   230  
   231  	return None, False
   232  }
   233  
   234  func (t *Object) Has(key String) Boolean {
   235  	_, exists := t.value[string(key)]
   236  
   237  	return NewBoolean(exists)
   238  }
   239  
   240  func (t *Object) MustGet(key String) core.Value {
   241  	val, _ := t.Get(key)
   242  
   243  	return val
   244  }
   245  
   246  func (t *Object) MustGetOr(key String, defaultValue core.Value) core.Value {
   247  	val, found := t.value[string(key)]
   248  
   249  	if found {
   250  		return val
   251  	}
   252  
   253  	return defaultValue
   254  }
   255  
   256  func (t *Object) Get(key String) (core.Value, Boolean) {
   257  	val, found := t.value[string(key)]
   258  
   259  	if found {
   260  		return val, NewBoolean(found)
   261  	}
   262  
   263  	return None, NewBoolean(found)
   264  }
   265  
   266  func (t *Object) Set(key String, value core.Value) {
   267  	if value != nil {
   268  		t.value[string(key)] = value
   269  	} else {
   270  		t.value[string(key)] = None
   271  	}
   272  }
   273  
   274  func (t *Object) Remove(key String) {
   275  	delete(t.value, string(key))
   276  }
   277  
   278  func (t *Object) Clone() core.Cloneable {
   279  	cloned := NewObject()
   280  
   281  	var value core.Value
   282  	var keyString String
   283  
   284  	for key := range t.value {
   285  		keyString = NewString(key)
   286  		value, _ = t.Get(keyString)
   287  
   288  		cloneable, ok := value.(core.Cloneable)
   289  
   290  		if ok {
   291  			value = cloneable.Clone()
   292  		}
   293  		cloned.Set(keyString, value)
   294  	}
   295  
   296  	return cloned
   297  }
   298  
   299  func (t *Object) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
   300  	if len(path) == 0 {
   301  		return None, nil
   302  	}
   303  
   304  	segmentIdx := 0
   305  	first, _ := t.Get(ToString(path[segmentIdx]))
   306  
   307  	if len(path) == 1 {
   308  		return first, nil
   309  	}
   310  
   311  	segmentIdx++
   312  
   313  	if first == None || first == nil {
   314  		return None, core.NewPathError(core.ErrInvalidPath, segmentIdx)
   315  	}
   316  
   317  	getter, ok := first.(core.Getter)
   318  
   319  	if !ok {
   320  		return GetIn(ctx, first, path[segmentIdx:])
   321  	}
   322  
   323  	return getter.GetIn(ctx, path[segmentIdx:])
   324  }