Added OrderedNavigableMap implementation

This commit is contained in:
Trial97
2020-05-05 14:11:48 +03:00
committed by Dan Christian Bogos
parent 36431268c5
commit 3b9b2895e0
32 changed files with 3131 additions and 344 deletions

View File

@@ -450,7 +450,7 @@ func (kdr *KamDlgReply) String() string {
// GetOptions returns the posible options
func (kev KamEvent) GetOptions() (mp map[string]interface{}) {
mp = make(map[string]interface{})
for k := range utils.CGROptionsSet.Data() {
for k := range utils.CGROptionsSet {
if val, has := kev[k]; has {
mp[k] = val
}

View File

@@ -429,7 +429,7 @@ func (cfg *CGRConfig) loadDataDBCfg(jsnCfg *CgrJsonCfg) (err error) {
// so we enforce it here
if cfg.dataDbCfg.DataDbType == utils.INTERNAL {
// overwrite only DataDBPartitions and leave other unmodified ( e.g. *diameter_messages, *closed_sessions, etc... )
for key := range utils.CacheDataDBPartitions.Data() {
for key := range utils.CacheDataDBPartitions {
if _, has := cfg.cacheCfg.Partitions[key]; has {
cfg.cacheCfg.Partitions[key] = &CacheParamCfg{Limit: 0,
TTL: time.Duration(0), StaticTTL: false, Precache: false}

View File

@@ -199,7 +199,7 @@ func (cfg *CGRConfig) checkConfigSanity() error {
return fmt.Errorf("<%s> %s needs to be != 0, received: %d", utils.CacheS, utils.CacheClosedSessions,
cfg.cacheCfg.Partitions[utils.CacheClosedSessions].Limit)
}
for alfld := range cfg.sessionSCfg.AlterableFields.Data() {
for alfld := range cfg.sessionSCfg.AlterableFields {
if utils.ProtectedSFlds.Has(alfld) {
return fmt.Errorf("<%s> the following protected field can't be altered by session: <%s>", utils.SessionS, alfld)
}

View File

@@ -97,7 +97,7 @@ type SessionSCfg struct {
ClientProtocol float64
ChannelSyncInterval time.Duration
TerminateAttempts int
AlterableFields *utils.StringSet
AlterableFields utils.StringSet
MinDurLowBalance time.Duration
SchedulerConns []string
STIRCfg *STIRcfg
@@ -681,7 +681,7 @@ func (aCfg *AsteriskAgentCfg) AsMapInterface() map[string]interface{} {
// STIRcfg the confuguration structure for STIR
type STIRcfg struct {
AllowedAttest *utils.StringSet
AllowedAttest utils.StringSet
PayloadMaxduration time.Duration
DefaultAttest string
PublicKeyPath string

View File

@@ -483,7 +483,7 @@ func (cdrS *CDRServer) processEvent(ev *utils.CGREventWithOpts,
}
}
}
procFlgs := make([]*utils.StringSet, len(cgrEvs)) // will save the flags for the reply here
procFlgs := make([]utils.StringSet, len(cgrEvs)) // will save the flags for the reply here
for i := range cgrEvs {
procFlgs[i] = utils.NewStringSet(nil)
}

View File

@@ -2415,7 +2415,7 @@ func (dm *DataManager) GetAttributeProfile(tenant, id string, cacheRead, cacheWr
}
}
isInline := false
for typeAttr := range utils.AttrInlineTypes.Data() {
for typeAttr := range utils.AttrInlineTypes {
if strings.HasPrefix(id, typeAttr) {
isInline = true
break

View File

@@ -199,16 +199,16 @@ func (fltr *Filter) Compile() (err error) {
return
}
var supportedFiltersType *utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix, utils.MetaSuffix,
var supportedFiltersType utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix, utils.MetaSuffix,
utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations,
utils.MetaEmpty, utils.MetaExists, utils.MetaLessThan, utils.MetaLessOrEqual,
utils.MetaGreaterThan, utils.MetaGreaterOrEqual, utils.MetaEqual,
utils.MetaNotEqual})
var needsFieldName *utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix,
var needsFieldName utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix,
utils.MetaSuffix, utils.MetaTimings, utils.MetaDestinations, utils.MetaLessThan,
utils.MetaEmpty, utils.MetaExists, utils.MetaLessOrEqual, utils.MetaGreaterThan,
utils.MetaGreaterOrEqual, utils.MetaEqual, utils.MetaNotEqual})
var needsValues *utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix,
var needsValues utils.StringSet = utils.NewStringSet([]string{utils.MetaString, utils.MetaPrefix,
utils.MetaSuffix, utils.MetaTimings, utils.MetaRSR, utils.MetaDestinations,
utils.MetaLessThan, utils.MetaLessOrEqual, utils.MetaGreaterThan, utils.MetaGreaterOrEqual,
utils.MetaEqual, utils.MetaNotEqual})

View File

@@ -181,7 +181,7 @@ func (me MapEvent) Clone() (mp MapEvent) {
// AsMapString returns a map[string]string out of mp, ignoring specific fields if needed
// most used when needing to export extraFields
func (me MapEvent) AsMapString(ignoredFlds *utils.StringSet) (mp map[string]string) {
func (me MapEvent) AsMapString(ignoredFlds utils.StringSet) (mp map[string]string) {
mp = make(map[string]string)
if ignoredFlds == nil {
ignoredFlds = utils.NewStringSet(nil)

View File

@@ -240,7 +240,7 @@ func (se *SafEvent) AsMapInterface() (mp map[string]interface{}) {
// AsMapString returns a map[string]string out of mp, ignoring specific fields if needed
// most used when needing to export extraFields
func (se *SafEvent) AsMapString(ignoredFlds *utils.StringSet) (mp map[string]string) {
func (se *SafEvent) AsMapString(ignoredFlds utils.StringSet) (mp map[string]string) {
se.RLock()
mp = se.Me.AsMapString(ignoredFlds)
se.RUnlock()

View File

@@ -1096,7 +1096,7 @@ func (iDB *InternalDB) GetCDRs(filter *utils.CDRsFilter, remove bool) (cdrs []*C
}
// find indexed fields
var cdrMpIDs *utils.StringSet
var cdrMpIDs utils.StringSet
// Apply string filter
for _, fltrSlc := range pairSlice {
if len(fltrSlc.ids) == 0 {
@@ -1173,7 +1173,7 @@ func (iDB *InternalDB) GetCDRs(filter *utils.CDRsFilter, remove bool) (cdrs []*C
}
paginatorOffsetCounter := 0
for key := range cdrMpIDs.Data() {
for key := range cdrMpIDs {
x, ok := iDB.db.Get(utils.CDRsTBL, key)
if !ok || x == nil {
return nil, 0, utils.ErrNotFound

View File

@@ -163,7 +163,7 @@ func (pi *ProcessedStirIdentity) VerifySignature(timeoutVal time.Duration) (err
// VerifyPayload returns if the payload is corectly populated
func (pi *ProcessedStirIdentity) VerifyPayload(originatorTn, originatorURI, destinationTn, destinationURI string,
hdrMaxDur time.Duration, attest *utils.StringSet) (err error) {
hdrMaxDur time.Duration, attest utils.StringSet) (err error) {
if !attest.Has(utils.META_ANY) && !attest.Has(pi.Payload.ATTest) {
return errors.New("wrong attest level")
}
@@ -221,7 +221,7 @@ func NewSTIRIdentity(header *utils.PASSporTHeader, payload *utils.PASSporTPayloa
// AuthStirShaken autentificates the given identity using STIR/SHAKEN
func AuthStirShaken(identity, originatorTn, originatorURI, destinationTn, destinationURI string,
attest *utils.StringSet, hdrMaxDur time.Duration) (err error) {
attest utils.StringSet, hdrMaxDur time.Duration) (err error) {
var pi *ProcessedStirIdentity
if pi, err = NewProcessedIdentity(identity); err != nil {
return

View File

@@ -308,7 +308,7 @@ func (sr *SRun) debitReserve(dur time.Duration, lastUsage *time.Duration) (rDur
}
// updateSRuns updates the SRuns event with the alterable fields (is not thread safe)
func (s *Session) updateSRuns(updEv engine.MapEvent, alterableFields *utils.StringSet) {
func (s *Session) updateSRuns(updEv engine.MapEvent, alterableFields utils.StringSet) {
if alterableFields.Size() == 0 {
return
}
@@ -323,7 +323,7 @@ func (s *Session) updateSRuns(updEv engine.MapEvent, alterableFields *utils.Stri
}
// UpdateSRuns updates the SRuns event with the alterable fields (is thread safe)
func (s *Session) UpdateSRuns(updEv engine.MapEvent, alterableFields *utils.StringSet) {
func (s *Session) UpdateSRuns(updEv engine.MapEvent, alterableFields utils.StringSet) {
if alterableFields.Size() == 0 { // do not lock if we can't update any field
return
}

View File

@@ -893,26 +893,6 @@ func RandomInteger(min, max int) int {
return math_rand.Intn(max-min) + min
}
// GetPathIndex returns the path and index if index present
// path[index]=>path,index
// path=>path,nil
func GetPathIndex(spath string) (opath string, idx *int) {
idxStart := strings.Index(spath, IdxStart)
if idxStart == -1 || !strings.HasSuffix(spath, IdxEnd) {
return spath, nil
}
slctr := spath[idxStart+1 : len(spath)-1]
opath = spath[:idxStart]
// if strings.HasPrefix(slctr, DynamicDataPrefix) {
// return
// }
idxVal, err := strconv.Atoi(slctr)
if err != nil {
return spath, nil
}
return opath, &idxVal
}
type GetFilterIndexesArgWithArgDispatcher struct {
*GetFilterIndexesArg
TenantArg

View File

@@ -1307,7 +1307,7 @@ func TestRandomInteger(t *testing.T) {
t.Errorf("same result over 3 attempts")
}
if a >= 100 || b >= 100 || c >= 100 {
t.Errorf("one of the numbers equals the (/are above) max limit")
t.Errorf("one of the numbers equals or it's above the max limit")
}
if a < 0 || b < 0 || c < 0 {
t.Errorf("one of the numbers are below min limit")

123
utils/dataprovider.go Normal file
View File

@@ -0,0 +1,123 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"net"
"strings"
)
// posible NMType
const (
NMDataType NMType = iota
NMMapType
NMSliceType
)
// DataProvider is a data source from multiple formats
type DataProvider interface {
String() string // printable version of data
FieldAsInterface(fldPath []string) (interface{}, error)
FieldAsString(fldPath []string) (string, error) // remove this
RemoteHost() net.Addr
}
// NavigableMapper is the interface supported by replies convertible to CGRReply
type NavigableMapper interface {
AsNavigableMap() NavigableMap2
}
// DPDynamicInterface returns the value of the field if the path is dynamic
func DPDynamicInterface(dnVal string, dP DataProvider) (interface{}, error) {
if strings.HasPrefix(dnVal, DynamicDataPrefix) {
dnVal = strings.TrimPrefix(dnVal, DynamicDataPrefix)
return dP.FieldAsInterface(strings.Split(dnVal, NestingSep))
}
return StringToInterface(dnVal), nil
}
// DPDynamicString returns the string value of the field if the path is dynamic
func DPDynamicString(dnVal string, dP DataProvider) (string, error) {
if strings.HasPrefix(dnVal, DynamicDataPrefix) {
dnVal = strings.TrimPrefix(dnVal, DynamicDataPrefix)
return dP.FieldAsString(strings.Split(dnVal, NestingSep))
}
return dnVal, nil
}
// NMType the type used for navigable Map
type NMType byte
// NMInterface the basic interface
type NMInterface interface {
String() string
Interface() interface{}
Field(path PathItems) (val NMInterface, err error)
Set(path PathItems, val NMInterface) (addedNew bool, err error)
Remove(path PathItems) (err error)
Type() NMType
Empty() bool
Len() int
}
// navMap subset of function for NM interface
type navMap interface {
Field(path PathItems) (val NMInterface, err error)
Set(path PathItems, val NMInterface) (addedNew bool, err error)
}
// AppendNavMapVal appends value to the map
func AppendNavMapVal(nm navMap, fldPath PathItems, val NMInterface) (err error) {
var prevItm NMInterface
var indx int
if prevItm, err = nm.Field(fldPath); err != nil {
if err != ErrNotFound {
return
}
} else {
indx = prevItm.Len()
}
fldPath[len(fldPath)-1].Index = &indx
_, err = nm.Set(fldPath, val)
return
}
// ComposeNavMapVal compose adds value to prevision item
func ComposeNavMapVal(nm navMap, fldPath PathItems, val NMInterface) (err error) {
var prevItmSlice NMInterface
var indx int
if prevItmSlice, err = nm.Field(fldPath); err != nil {
if err != ErrNotFound {
return
}
} else {
indx = prevItmSlice.Len() - 1
var prevItm NMInterface
if prevItm, err = prevItmSlice.Field(PathItems{{Index: &indx}}); err != nil {
if err != ErrNotFound {
return
}
} else if _, err = val.Set(nil, NewNMData(IfaceAsString(prevItm.Interface())+IfaceAsString(val.Interface()))); err != nil {
return
}
}
fldPath[len(fldPath)-1].Index = &indx
_, err = nm.Set(fldPath, val)
return
}

190
utils/dataprovider_test.go Normal file
View File

@@ -0,0 +1,190 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"reflect"
"testing"
)
func TestDPDynamicInterface(t *testing.T) {
nm := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
var expected interface{} = "Field5[1]"
if rply, err := DPDynamicInterface("Field5[1]", nm); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
expected = 101
if rply, err := DPDynamicInterface("~Field5[1]", nm); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %v ,received: %v", expected, rply)
}
}
func TestDPDynamicString(t *testing.T) {
nm := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
var expected interface{} = "Field5[1]"
if rply, err := DPDynamicString("Field5[1]", nm); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
expected = "101"
if rply, err := DPDynamicString("~Field5[1]", nm); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %v ,received: %v", expected, rply)
}
}
func TestAppendNavMapVal(t *testing.T) {
nm := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
expected := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101), NewNMData(18)},
}
if err := AppendNavMapVal(nm, PathItems{{Field: "Field5"}}, NewNMData(18)); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, nm) {
t.Errorf("Expected %v ,received: %v", expected, nm)
}
if err := AppendNavMapVal(nm, nil, NewNMData(18)); err != ErrWrongPath {
t.Error(err)
}
}
func TestComposeNavMapVal(t *testing.T) {
nm := NavigableMap2{
"Field4": &NMSlice{},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if err := ComposeNavMapVal(nm, nil, NewNMData(18)); err != ErrWrongPath {
t.Error(err)
}
if err := ComposeNavMapVal(nm, PathItems{{Field: "Field4"}}, NewNMData(18)); err != ErrWrongPath {
t.Error(err)
}
expected := NavigableMap2{
"Field4": &NMSlice{},
"Field5": &NMSlice{NewNMData(10), NewNMData("10118")},
}
if err := ComposeNavMapVal(nm, PathItems{{Field: "Field5"}}, NewNMData(18)); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, nm) {
t.Errorf("Expected %v ,received: %v", expected, nm)
}
expected = NavigableMap2{
"Field4": &NMSlice{},
"Field5": &NMSlice{NewNMData(10), NewNMData("10118")},
"Field6": &NMSlice{NewNMData(10)},
}
if err := ComposeNavMapVal(nm, PathItems{{Field: "Field6"}}, NewNMData(10)); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expected, nm) {
t.Errorf("Expected %v ,received: %v", expected, nm)
}
nm = NavigableMap2{
"Field4": NewNMData(1),
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if err := ComposeNavMapVal(nm, PathItems{{Field: "Field4"}}, NewNMData(10)); err != ErrNotImplemented {
t.Error(err)
}
if err := ComposeNavMapVal(nm, PathItems{{Field: "Field5"}}, &mockNMInterface{data: 10}); err != ErrNotImplemented {
t.Error(err)
}
}
// mock NMInterface structure
type mockNMInterface struct{ data interface{} }
func (nmi *mockNMInterface) String() string {
return IfaceAsString(nmi.data)
}
// Interface returns the wraped interface
func (nmi *mockNMInterface) Interface() interface{} {
return nmi.data
}
// Field not implemented only used in order to implement the NM interface
func (nmi *mockNMInterface) Field(path PathItems) (val NMInterface, err error) {
return nil, ErrNotImplemented
}
// Set not implemented
func (nmi *mockNMInterface) Set(path PathItems, val NMInterface) (a bool, err error) {
return false, ErrNotImplemented
}
// Remove not implemented only used in order to implement the NM interface
func (nmi *mockNMInterface) Remove(path PathItems) (err error) {
return ErrNotImplemented
}
// Type returns the type of the NM interface
func (nmi *mockNMInterface) Type() NMType {
return NMDataType
}
// Empty returns true if the NM is empty(no data)
func (nmi *mockNMInterface) Empty() bool {
return nmi == nil || nmi.data == nil
}
// GetField not implemented only used in order to implement the NM interface
func (nmi *mockNMInterface) GetField(path PathItem) (val NMInterface, err error) {
return nil, ErrNotImplemented
}
// SetField not implemented
func (nmi *mockNMInterface) SetField(path PathItem, val NMInterface) (err error) {
return ErrNotImplemented
}
// Len not implemented only used in order to implement the NM interface
func (nmi *mockNMInterface) Len() int {
return 0
}

View File

@@ -72,6 +72,7 @@ var (
ErrNoDatabaseConn = errors.New("NO_DATA_BASE_CONNECTION")
ErrMaxIncrementsExceeded = errors.New("MAX_INCREMENTS_EXCEEDED")
ErrIndexOutOfBounds = errors.New("INDEX_OUT_OF_BOUNDS")
ErrWrongPath = errors.New("WRONG_PATH")
ErrMap = map[string]error{
ErrNoMoreData.Error(): ErrNoMoreData,
@@ -111,6 +112,7 @@ var (
ErrNoDatabaseConn.Error(): ErrNoDatabaseConn,
ErrMaxIncrementsExceeded.Error(): ErrMaxIncrementsExceeded,
ErrIndexOutOfBounds.Error(): ErrIndexOutOfBounds,
ErrWrongPath.Error(): ErrWrongPath,
}
)

View File

@@ -19,254 +19,181 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
import (
"fmt"
"strings"
"net"
)
// DataStorage is the new DataProvider
type DataStorage interface {
String() string // printable version of data
Get(fldPath []string) (interface{}, error)
GetString(fldPath []string) (string, error)
Set(fldPath []string, val interface{}) error
Remove(fldPath []string) error
GetKeys(nesteed bool) []string
// RemoteHost() net.Addr
// NavigableMap2 is the basic map of NM interface
type NavigableMap2 map[string]NMInterface
func (nm NavigableMap2) String() (out string) {
for k, v := range nm {
out += ",\"" + k + "\":" + v.String()
}
if len(out) == 0 {
return "{}"
}
out = out[1:]
return "{" + out + "}"
}
// MapStorage is the basic DataStorage
type MapStorage map[string]interface{}
// String returns the map as json string
func (ms *MapStorage) String() string { return ToJSON(ms) }
// Get returns the value from the path
func (ms *MapStorage) Get(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
err = ErrNotFound
return
}
var has bool
if val, has = (*ms)[fldPath[0]]; !has {
err = ErrNotFound
return
}
if len(fldPath) == 1 {
return
}
ds, ok := val.(DataStorage)
if !ok {
err = fmt.Errorf("Wrong type")
return
}
return ds.Get(fldPath[1:])
// Interface returns itself
func (nm NavigableMap2) Interface() interface{} {
return nm
}
// GetString returns thevalue from path as string
func (ms *MapStorage) GetString(fldPath []string) (str string, err error) {
var val interface{}
if val, err = ms.Get(fldPath); err != nil {
return
// Field returns the item on the given path
func (nm NavigableMap2) Field(path PathItems) (val NMInterface, err error) {
if len(path) == 0 {
return nil, ErrWrongPath
}
return IfaceAsString(val), nil
}
// Set sets the value at the given path
func (ms *MapStorage) Set(fldPath []string, val interface{}) (err error) {
if len(fldPath) == 0 {
return fmt.Errorf("Wrong path")
}
if len(fldPath) == 1 {
(*ms)[fldPath[0]] = val
return
}
nMap := &MapStorage{}
(*ms)[fldPath[0]] = nMap
return nMap.Set(fldPath[1:], val)
}
// GetKeys returns all the keys from map
func (ms *MapStorage) GetKeys(nesteed bool) (keys []string) {
for k, v := range *ms {
keys = append(keys, k)
if !nesteed {
continue
}
ds, ok := v.(DataStorage)
if !ok {
continue
}
for _, dsKey := range ds.GetKeys(nesteed) {
keys = append(keys, k+NestingSep+dsKey)
}
}
return
}
// Remove removes the item at path
func (ms *MapStorage) Remove(fldPath []string) (err error) {
if len(fldPath) == 0 {
return fmt.Errorf("Wrong path")
}
var val interface{}
var has bool
if val, has = (*ms)[fldPath[0]]; !has {
return // ignore (already removed)
}
if len(fldPath) == 1 {
delete(*ms, fldPath[0])
return
}
ds, ok := val.(DataStorage)
if !ok {
err = fmt.Errorf("Wrong type")
return
}
return ds.Remove(fldPath[1:])
}
// NavigableMap is a DataStorage
type NavigableMap map[string]DataStorage
// String returns the map as json string
func (nm *NavigableMap) String() string { return ToJSON(nm) }
// Get returns the value from the path
func (nm *NavigableMap) Get(fldPath []string) (val interface{}, err error) {
if len(fldPath) == 0 {
err = ErrNotFound
return
}
ds, has := (*nm)[fldPath[0]]
el, has := nm[path[0].Field]
if !has {
err = ErrNotFound
return
return nil, ErrNotFound
}
if len(fldPath) == 1 {
val = ds
return
if len(path) == 1 && path[0].Index == nil {
return el, nil
}
switch el.Type() {
default:
return nil, ErrNotFound
case NMMapType:
if path[0].Index != nil {
return nil, ErrNotFound
}
return el.Field(path[1:])
case NMSliceType:
return el.Field(path)
}
return ds.Get(fldPath[1:])
}
// GetString returns thevalue from path as string
func (nm *NavigableMap) GetString(fldPath []string) (str string, err error) {
// Set sets the value for the given path
func (nm NavigableMap2) Set(path PathItems, val NMInterface) (added bool, err error) {
if len(path) == 0 {
return false, ErrWrongPath
}
nmItm, has := nm[path[0].Field]
if path[0].Index != nil { // has index, should be a slice which is kinda part of our map, hence separate handling
if !has {
nmItm = &NMSlice{}
if _, err = nmItm.Set(path, val); err != nil {
return
}
nm[path[0].Field] = nmItm
added = true
return
}
if nmItm.Type() != NMSliceType {
return false, ErrWrongPath
}
return nmItm.Set(path, val)
}
// standard handling
if len(path) == 1 { // always overwrite for single path
nm[path[0].Field] = val
if !has {
added = true
}
return
}
// from here we should deal only with navmaps due to multiple path
if !has {
nmItm = NavigableMap2{}
nm[path[0].Field] = nmItm
}
if nmItm.Type() != NMMapType { // do not try to overwrite an interface
return false, ErrWrongPath
}
return nmItm.Set(path[1:], val)
}
// Remove removes the item for the given path
func (nm NavigableMap2) Remove(path PathItems) (err error) {
if len(path) == 0 {
return ErrWrongPath
}
el, has := nm[path[0].Field]
if !has {
return // already removed
}
if len(path) == 1 {
if path[0].Index != nil {
if el.Type() != NMSliceType {
return ErrWrongPath
}
// this should not return error
// but in case it does we propagate it further
err = el.Remove(path)
if el.Empty() {
delete(nm, path[0].Field)
}
return
}
delete(nm, path[0].Field)
return
}
if path[0].Index != nil {
if el.Type() != NMSliceType {
return ErrWrongPath
}
if err = el.Remove(path); err != nil {
return
}
if el.Empty() {
delete(nm, path[0].Field)
}
return
}
if el.Type() != NMMapType {
return ErrWrongPath
}
if err = el.Remove(path[1:]); err != nil {
return
}
if el.Empty() {
delete(nm, path[0].Field)
}
return
}
// Type returns the type of the NM map
func (nm NavigableMap2) Type() NMType {
return NMMapType
}
// Empty returns true if the NM is empty(no data)
func (nm NavigableMap2) Empty() bool {
return nm == nil || len(nm) == 0
}
// Len returns the lenght of the map
func (nm NavigableMap2) Len() int {
return len(nm)
}
// FieldAsInterface returns the interface at the path
// Is used by AgentRequest FieldAsInterface
func (nm NavigableMap2) FieldAsInterface(fldPath []string) (str interface{}, err error) {
var nmi NMInterface
if nmi, err = nm.Field(NewPathToItem(fldPath)); err != nil {
return
}
return nmi.Interface(), nil
}
// FieldAsString returns the string at the path
// Used only to implement the DataProvider interface
func (nm NavigableMap2) FieldAsString(fldPath []string) (str string, err error) {
var val interface{}
if val, err = nm.Get(fldPath); err != nil {
val, err = nm.FieldAsInterface(fldPath)
if err != nil {
return
}
return IfaceAsString(val), nil
}
// Set sets the value at the given path
func (nm *NavigableMap) Set(fldPath []string, val interface{}) (err error) {
if len(fldPath) == 0 {
return fmt.Errorf("Wrong path")
}
if len(fldPath) == 1 {
ds, ok := val.(DataStorage)
if !ok {
return fmt.Errorf("Wrong type")
}
(*nm)[fldPath[0]] = ds
return
}
if _, has := (*nm)[fldPath[0]]; !has {
(*nm)[fldPath[0]] = &MapStorage{}
}
return (*nm)[fldPath[0]].Set(fldPath[1:], val)
}
// GetKeys returns all the keys from map
func (nm *NavigableMap) GetKeys(nesteed bool) (keys []string) {
for k, v := range *nm {
keys = append(keys, k)
if !nesteed {
continue
}
for _, dsKey := range v.GetKeys(nesteed) {
keys = append(keys, k+NestingSep+dsKey)
}
}
return
}
// Remove removes the item at path
func (nm *NavigableMap) Remove(fldPath []string) (err error) {
if len(fldPath) == 0 {
return fmt.Errorf("Wrong path")
}
var val DataStorage
var has bool
if val, has = (*nm)[fldPath[0]]; !has {
return // ignore (already removed)
}
if len(fldPath) == 1 {
delete(*nm, fldPath[0])
return
}
return val.Remove(fldPath[1:])
}
// NewOrderedNavigableMap initializates a structure of OrderedNavigableMap with a NavigableMap
func NewOrderedNavigableMap(nm *NavigableMap) *OrderedNavigableMap {
if nm == nil {
return &OrderedNavigableMap{
nm: &NavigableMap{},
order: [][]string{},
}
}
keys := nm.GetKeys(true)
order := make([][]string, len(keys))
for i, k := range keys {
order[i] = strings.Split(k, NestingSep)
}
return &OrderedNavigableMap{
nm: nm,
order: order,
}
}
// OrderedNavigableMap is the same as NavigableMap but keeps the order of fields
type OrderedNavigableMap struct {
nm *NavigableMap
order [][]string
}
// String returns the map as json string
func (onm *OrderedNavigableMap) String() string { return ToJSON(onm.nm) }
// Get returns the value from the path
func (onm *OrderedNavigableMap) Get(fldPath []string) (val interface{}, err error) {
return onm.nm.Get(fldPath)
}
// GetString returns thevalue from path as string
func (onm *OrderedNavigableMap) GetString(fldPath []string) (str string, err error) {
return onm.nm.GetString(fldPath)
}
// Set sets the value at the given path
func (onm *OrderedNavigableMap) Set(fldPath []string, val interface{}) (err error) {
if err = onm.nm.Set(fldPath, val); err == nil {
onm.order = append(onm.order, fldPath)
}
return
}
// GetKeys returns all the keys from map
func (onm *OrderedNavigableMap) GetKeys(nesteed bool) (keys []string) {
keys = make([]string, len(onm.order))
for i, k := range onm.order {
keys[i] = strings.Join(k, NestingSep)
}
return
}
// Remove removes the item at path
func (onm *OrderedNavigableMap) Remove(fldPath []string) (err error) {
if len(fldPath) == 0 {
return fmt.Errorf("Wrong path")
}
return onm.nm.Remove(fldPath)
// RemoteHost is part of dataStorage interface
func (NavigableMap2) RemoteHost() net.Addr {
return LocalAddr()
}

440
utils/navigablemap_test.go Normal file
View File

@@ -0,0 +1,440 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"reflect"
"testing"
)
func TestNavigableMap2String(t *testing.T) {
var nm NMInterface = NavigableMap2{"Field1": NewNMData("1001")}
expected := `{"Field1":1001}`
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
nm = NavigableMap2{}
expected = `{}`
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
}
func TestNavigableMap2Interface(t *testing.T) {
nm := NavigableMap2{"Field1": NewNMData("1001"), "Field2": NewNMData("1003")}
expected := NavigableMap2{"Field1": NewNMData("1001"), "Field2": NewNMData("1003")}
if rply := nm.Interface(); !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %s ,received: %s", ToJSON(expected), ToJSON(rply))
}
}
func TestNavigableMap2Field(t *testing.T) {
nm := NavigableMap2{}
if _, err := nm.Field(PathItems{{}}); err != ErrNotFound {
t.Error(err)
}
nm = NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if _, err := nm.Field(nil); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Field(PathItems{{Field: "NaN"}}); err != ErrNotFound {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "Field1"}}); err != nil {
t.Error(err)
} else if val.Interface() != "1001" {
t.Errorf("Expected %q ,received: %q", "1001", val.Interface())
}
if _, err := nm.Field(PathItems{{Field: "Field1", Index: IntPointer(0)}}); err != ErrNotFound {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "Field5", Index: IntPointer(0)}}); err != nil {
t.Error(err)
} else if val.Interface() != 10 {
t.Errorf("Expected %q ,received: %q", 10, val.Interface())
}
if _, err := nm.Field(PathItems{{Field: "Field3", Index: IntPointer(0)}}); err != ErrNotFound {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "Field3"}, {Field: "Field4"}}); err != nil {
t.Error(err)
} else if val.Interface() != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val.Interface())
}
}
func TestNavigableMap2Set(t *testing.T) {
nm := NavigableMap2{}
if _, err := nm.Set(nil, nil); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(10)}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
expected := NavigableMap2{"Field1": &NMSlice{NewNMData("1001")}}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(0)}}, NewNMData("1001")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001")},
"Field2": NewNMData("1002"),
}
if _, err := nm.Set(PathItems{{Field: "Field2"}}, NewNMData("1002")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if _, err := nm.Set(PathItems{{Field: "Field2", Index: IntPointer(1)}}, NewNMData("1003")); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001"), NewNMData("1003")},
"Field2": NewNMData("1002"),
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(1)}}, NewNMData("1003")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001"), NewNMData("1003")},
"Field2": NewNMData("1004"),
}
if _, err := nm.Set(PathItems{{Field: "Field2"}}, NewNMData("1004")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if _, err := nm.Set(PathItems{{Field: "Field3", Index: IntPointer(10)}, {}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001"), NewNMData("1003")},
"Field2": NewNMData("1004"),
"Field3": &NMSlice{NavigableMap2{"Field4": NewNMData("1005")}},
}
if _, err := nm.Set(PathItems{{Field: "Field3", Index: IntPointer(0)}, {Field: "Field4"}}, NewNMData("1005")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if _, err := nm.Set(PathItems{{Field: "Field5"}, {Field: "Field6", Index: IntPointer(10)}}, NewNMData("1006")); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001"), NewNMData("1003")},
"Field2": NewNMData("1004"),
"Field3": &NMSlice{NavigableMap2{"Field4": NewNMData("1005")}},
"Field5": NavigableMap2{"Field6": NewNMData("1006")},
}
if _, err := nm.Set(PathItems{{Field: "Field5"}, {Field: "Field6"}}, NewNMData("1006")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if _, err := nm.Set(PathItems{{Field: "Field2", Index: IntPointer(0)}, {}}, NewNMData("1006")); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001"), NewNMData("1003"), NavigableMap2{"Field6": NewNMData("1006")}},
"Field2": NewNMData("1004"),
"Field3": &NMSlice{NavigableMap2{"Field4": NewNMData("1005")}},
"Field5": NavigableMap2{"Field6": NewNMData("1006")},
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(2)}, {Field: "Field6"}}, NewNMData("1006")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if _, err := nm.Set(PathItems{{Field: "Field2"}, {}}, NewNMData("1006")); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": &NMSlice{NewNMData("1001"), NewNMData("1003"), NavigableMap2{"Field6": NewNMData("1006")}},
"Field2": NewNMData("1004"),
"Field3": &NMSlice{NavigableMap2{"Field4": NewNMData("1005")}},
"Field5": NavigableMap2{"Field6": NewNMData("1007")},
}
if _, err := nm.Set(PathItems{{Field: "Field5"}, {Field: "Field6"}}, NewNMData("1007")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
}
func TestNavigableMap2Type(t *testing.T) {
var nm NMInterface = NavigableMap2{}
if nm.Type() != NMMapType {
t.Errorf("Expected %v ,received: %v", NMMapType, nm.Type())
}
}
func TestNavigableMap2Empty(t *testing.T) {
var nm NMInterface = NavigableMap2{}
if !nm.Empty() {
t.Error("Expected empty type")
}
nm = NavigableMap2{"Field1": NewNMData("1001")}
if nm.Empty() {
t.Error("Expected not empty type")
}
}
func TestNavigableMap2Len(t *testing.T) {
var nm NMInterface = NavigableMap2{}
if rply := nm.Len(); rply != 0 {
t.Errorf("Expected 0 ,received: %v", rply)
}
nm = NavigableMap2{"Field1": NewNMData("1001")}
if rply := nm.Len(); rply != 1 {
t.Errorf("Expected 1 ,received: %v", rply)
}
}
func TestNavigableMap2Remove(t *testing.T) {
nm := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if err := nm.Remove(nil); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(PathItems{}); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(PathItems{{Field: "field"}}); err != nil {
t.Error(err)
}
if err := nm.Remove(PathItems{{Index: IntPointer(-1)}, {}}); err != nil {
t.Error(err)
}
expected := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
expected = NavigableMap2{
"Field1": NewNMData("1001"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if err := nm.Remove(PathItems{{Field: "Field2"}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
expected = NavigableMap2{
"Field1": NewNMData("1001"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(101)},
}
if err := nm.Remove(PathItems{{Field: "Field5", Index: IntPointer(0)}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if err := nm.Remove(PathItems{{Field: "Field1", Index: IntPointer(0)}, {}}); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": NewNMData("1001"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
}
if err := nm.Remove(PathItems{{Field: "Field5", Index: IntPointer(0)}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
nm = NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NavigableMap2{"Field42": NewNMData("Val2")}},
}
if err := nm.Remove(PathItems{{Field: "Field5", Index: IntPointer(0)}, {Field: "Field42", Index: IntPointer(0)}}); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
}
if err := nm.Remove(PathItems{{Field: "Field5", Index: IntPointer(0)}, {Field: "Field42"}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if err := nm.Remove(PathItems{{Field: "Field1"}, {}}); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(PathItems{{Field: "Field3"}, {Field: "Field4", Index: IntPointer(0)}}); err != ErrWrongPath {
t.Error(err)
}
expected = NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
}
if err := nm.Remove(PathItems{{Field: "Field3"}, {Field: "Field4"}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
}
func TestNavigableMap2GetSet(t *testing.T) {
var nm NMInterface = NavigableMap2{
"Field1": NewNMData(10),
"Field2": &NMSlice{
NewNMData("1001"),
NavigableMap2{
"Account": &NMSlice{NewNMData(10), NewNMData(11)},
},
},
"Field3": NavigableMap2{
"Field4": NavigableMap2{
"Field5": NewNMData(5),
},
},
}
path := PathItems{{Field: "Field1"}}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != 10 {
t.Errorf("Expected %q ,received: %q", 10, val.Interface())
}
path = PathItems{{Field: "Field3"}, {Field: "Field4"}, {Field: "Field5"}}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != 5 {
t.Errorf("Expected %q ,received: %q", 5, val.Interface())
}
path = PathItems{{Field: "Field2", Index: IntPointer(2)}}
if _, err := nm.Set(path, NewNMData("500")); err != nil {
t.Error(err)
}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != "500" {
t.Errorf("Expected %q ,received: %q", "500", val.Interface())
}
path = PathItems{{Field: "Field2", Index: IntPointer(1)}, {Field: "Account"}}
if _, err := nm.Set(path, NewNMData("5")); err != nil {
t.Error(err)
}
path = PathItems{{Field: "Field2", Index: IntPointer(1)}, {Field: "Account"}}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != "5" {
t.Errorf("Expected %q ,received: %q", "5", val.Interface())
}
path = PathItems{{Field: "Field2", Index: IntPointer(1)}, {Field: "Account", Index: IntPointer(0)}}
if _, err := nm.Field(path); err != ErrNotFound {
t.Error(err)
}
}
func TestNavigableMap2FieldAsInterface(t *testing.T) {
nm := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if _, err := nm.FieldAsInterface(nil); err != ErrWrongPath {
t.Error(err)
}
if val, err := nm.FieldAsInterface([]string{"Field3", "Field4"}); err != nil {
t.Error(err)
} else if val != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val)
}
if val, err := nm.FieldAsInterface([]string{"Field5[0]"}); err != nil {
t.Error(err)
} else if val != 10 {
t.Errorf("Expected %q ,received: %q", 10, val)
}
}
func TestNavigableMap2FieldAsString(t *testing.T) {
nm := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
if _, err := nm.FieldAsString(nil); err != ErrWrongPath {
t.Error(err)
}
if val, err := nm.FieldAsString([]string{"Field3", "Field4"}); err != nil {
t.Error(err)
} else if val != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val)
}
if val, err := nm.FieldAsString([]string{"Field5[0]"}); err != nil {
t.Error(err)
} else if val != "10" {
t.Errorf("Expected %q ,received: %q", "10", val)
}
}
func TestNavigableMapRemote(t *testing.T) {
nm := NavigableMap2{"Field1": NewNMData("1001")}
eOut := LocalAddr()
if rcv := nm.RemoteHost(); !reflect.DeepEqual(eOut, rcv) {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
}

70
utils/nmdata.go Normal file
View File

@@ -0,0 +1,70 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
// NewNMData returns the interface wraped in NMInterface struture
func NewNMData(val interface{}) *NMData { return &NMData{data: val} }
// NMData most basic NM structure
type NMData struct{ data interface{} }
func (nmi *NMData) String() string {
return IfaceAsString(nmi.data)
}
// Interface returns the wraped interface
func (nmi *NMData) Interface() interface{} {
return nmi.data
}
// Field is not implemented only used in order to implement the NM interface
func (nmi *NMData) Field(path PathItems) (val NMInterface, err error) {
return nil, ErrNotImplemented
}
// Set sets the wraped interface when the path is empty
// This behaivior is in order to modify the wraped interface
// witout aserting the type of the NMInterface
func (nmi *NMData) Set(path PathItems, val NMInterface) (addedNew bool, err error) {
if len(path) != 0 {
return false, ErrWrongPath
}
nmi.data = val.Interface()
return
}
// Remove is not implemented only used in order to implement the NM interface
func (nmi *NMData) Remove(path PathItems) (err error) {
return ErrNotImplemented
}
// Type returns the type of the NM interface
func (nmi *NMData) Type() NMType {
return NMDataType
}
// Empty returns true if the NM is empty(no data)
func (nmi *NMData) Empty() bool {
return nmi == nil || nmi.data == nil
}
// Len is not implemented only used in order to implement the NM interface
func (nmi *NMData) Len() int {
return 0
}

109
utils/nmdata_test.go Normal file
View File

@@ -0,0 +1,109 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"reflect"
"testing"
)
func TestNewNMInterface(t *testing.T) {
nm2 := NewNMData("1001")
expectednm := &NMData{data: "1001"}
if !reflect.DeepEqual(expectednm, nm2) {
t.Errorf("Expected %v ,received: %v", ToJSON(expectednm), ToJSON(nm2))
}
var nm NMInterface = nm2
expected := "1001"
if rply := nm.Interface(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
if nm2.data != expected {
t.Errorf("Expected %q ,received: %q", expected, nm2.data)
}
}
func TestNMDataLen(t *testing.T) {
var nm NMInterface = NewNMData("1001")
if rply := nm.Len(); rply != 0 {
t.Errorf("Expected 0 ,received: %v", rply)
}
}
func TestNMDataString(t *testing.T) {
var nm NMInterface = NewNMData("1001")
expected := "1001"
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
}
func TestNMDataInterface(t *testing.T) {
var nm NMInterface = NewNMData("1001")
expected := "1001"
if rply := nm.Interface(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
}
func TestNMDataField(t *testing.T) {
var nm NMInterface = NewNMData("1001")
if _, err := nm.Field(nil); err != ErrNotImplemented {
t.Error(err)
}
}
func TestNMDataRemove(t *testing.T) {
var nm NMInterface = NewNMData("1001")
if err := nm.Remove(nil); err != ErrNotImplemented {
t.Error(err)
}
}
func TestNMDataEmpty(t *testing.T) {
var nm NMInterface = NewNMData("1001")
if nm.Empty() {
t.Error("Expected not empty type")
}
nm = NewNMData(nil)
if !nm.Empty() {
t.Error("Expected empty type")
}
}
func TestNMDataType(t *testing.T) {
var nm NMInterface = NewNMData("1001")
if nm.Type() != NMDataType {
t.Errorf("Expected %v ,received: %v", NMDataType, nm.Type())
}
}
func TestNMDataSet(t *testing.T) {
var nm NMInterface = NewNMData("1001")
if _, err := nm.Set(PathItems{{}}, nil); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(nil, NewNMData("1002")); err != nil {
t.Error(err)
}
expected := "1002"
if rply := nm.Interface(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
}

140
utils/nmslice.go Normal file
View File

@@ -0,0 +1,140 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
// NMSlice is the basic slice of NM interface
type NMSlice []NMInterface
func (nms *NMSlice) String() (out string) {
for _, v := range *nms {
out += "," + v.String()
}
if len(out) == 0 {
return "[]"
}
out = out[1:]
return "[" + out + "]"
}
// Interface returns itself
func (nms *NMSlice) Interface() interface{} {
return nms
}
// Field returns the item on the given path
// for NMSlice only the Index field is considered
func (nms *NMSlice) Field(path PathItems) (val NMInterface, err error) {
if len(path) == 0 {
return nil, ErrWrongPath
}
if nms.Empty() || path[0].Index == nil {
return nil, ErrNotFound
}
idx := *path[0].Index
if idx < 0 {
idx = len(*nms) + idx
}
if idx < 0 || idx >= len(*nms) {
return nil, ErrNotFound
}
if len(path) == 1 {
return (*nms)[idx], nil
}
return (*nms)[idx].Field(path[1:])
}
// Set sets the value for the given index
func (nms *NMSlice) Set(path PathItems, val NMInterface) (addedNew bool, err error) {
if len(path) == 0 || path[0].Index == nil {
return false, ErrWrongPath
}
idx := *path[0].Index
if idx == len(*nms) { // append element
addedNew = true
if len(path) == 1 {
*nms = append(*nms, val)
return
}
nel := NavigableMap2{}
if _, err = nel.Set(path[1:], val); err != nil {
return
}
*nms = append(*nms, nel)
return
}
if idx < 0 {
idx = len(*nms) + idx
}
if idx < 0 || idx >= len(*nms) {
return false, ErrWrongPath
}
path[0].Index = &idx
if len(path) == 1 {
(*nms)[idx] = val
return
}
if (*nms)[idx].Type() == NMSliceType {
return false, ErrWrongPath
}
return (*nms)[idx].Set(path[1:], val)
}
// Remove removes the item for the given index
func (nms *NMSlice) Remove(path PathItems) (err error) {
if len(path) == 0 || path[0].Index == nil {
return ErrWrongPath
}
idx := *path[0].Index
if idx < 0 {
idx = len(*nms) + idx
}
if idx < 0 || idx >= len(*nms) { // already removed
return
}
path[0].Index = &idx
if len(path) == 1 {
*nms = append((*nms)[:idx], (*nms)[idx+1:]...)
return
}
if (*nms)[idx].Type() != NMMapType {
return ErrWrongPath
}
if err = (*nms)[idx].Remove(path[1:]); err != nil {
return
}
if (*nms)[idx].Empty() {
*nms = append((*nms)[:idx], (*nms)[idx+1:]...)
}
return
}
// Type returns the type of the NM slice
func (nms NMSlice) Type() NMType {
return NMSliceType
}
// Empty returns true if the NM is empty(no data)
func (nms NMSlice) Empty() bool {
return nms == nil || len(nms) == 0
}
// Len returns the lenght of the slice
func (nms *NMSlice) Len() int {
return len(*nms)
}

206
utils/nmslice_test.go Normal file
View File

@@ -0,0 +1,206 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"reflect"
"testing"
)
func TestNMSliceString(t *testing.T) {
var nm NMInterface = &NMSlice{NewNMData("1001"), NewNMData("1003")}
expected := "[1001,1003]"
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
nm = &NMSlice{}
expected = `[]`
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
}
func TestNMSliceInterface(t *testing.T) {
nm := &NMSlice{NewNMData("1001"), NewNMData("1003")}
expected := &NMSlice{NewNMData("1001"), NewNMData("1003")}
if rply := nm.Interface(); !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %s ,received: %s", ToJSON(expected), ToJSON(rply))
}
}
func TestNMSliceField(t *testing.T) {
nm := &NMSlice{}
if _, err := nm.Field(PathItems{{}}); err != ErrNotFound {
t.Error(err)
}
nm = &NMSlice{
NewNMData("1001"),
NewNMData("1003"),
&NavigableMap2{"Field1": NewNMData("Val")},
}
if _, err := nm.Field(PathItems{{}}); err != ErrNotFound {
t.Error(err)
}
if _, err := nm.Field(PathItems{{Index: IntPointer(4)}}); err != ErrNotFound {
t.Error(err)
}
if _, err := nm.Field(nil); err != ErrWrongPath {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "None", Index: IntPointer(-1)}, {Field: "Field1"}}); err != nil {
t.Error(err)
} else if val.Interface() != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val.Interface())
}
if val, err := nm.Field(PathItems{{Field: "1234", Index: IntPointer(1)}}); err != nil {
t.Error(err)
} else if val.Interface() != "1003" {
t.Errorf("Expected %q ,received: %q", "Val", val.Interface())
}
}
func TestNMSliceSet(t *testing.T) {
nm := &NMSlice{}
if _, err := nm.Set(nil, nil); err != ErrWrongPath {
t.Error(err)
}
expected := &NMSlice{NewNMData("1001")}
if _, err := nm.Set(PathItems{{Field: "1234", Index: IntPointer(0)}}, NewNMData("1001")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if _, err := nm.Set(PathItems{{Field: "1234", Index: IntPointer(1)}, {Field: "Field1", Index: IntPointer(1)}},
NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
expected = &NMSlice{NewNMData("1001"), NavigableMap2{"Field1": NewNMData("1001")}}
if _, err := nm.Set(PathItems{{Field: "1234", Index: IntPointer(1)}, {Field: "Field1"}},
NewNMData("1001")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
expected = &NMSlice{NewNMData("1001"), NewNMData("1001")}
if _, err := nm.Set(PathItems{{Field: "1234", Index: IntPointer(-1)}}, NewNMData("1001")); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
nm = &NMSlice{&NMSlice{}}
if _, err := nm.Set(PathItems{{Field: "1234", Index: IntPointer(0)}, {}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
}
func TestNMSliceType(t *testing.T) {
var nm NMInterface = &NMSlice{}
if nm.Type() != NMSliceType {
t.Errorf("Expected %v ,received: %v", NMSliceType, nm.Type())
}
}
func TestNMSliceEmpty(t *testing.T) {
var nm NMInterface = &NMSlice{}
if !nm.Empty() {
t.Error("Expected empty type")
}
nm = &NMSlice{NewNMData("1001")}
if nm.Empty() {
t.Error("Expected not empty type")
}
}
func TestNMSliceLen(t *testing.T) {
var nm NMInterface = &NMSlice{}
if rply := nm.Len(); rply != 0 {
t.Errorf("Expected 0 ,received: %v", rply)
}
nm = &NMSlice{NewNMData("1001")}
if rply := nm.Len(); rply != 1 {
t.Errorf("Expected 1 ,received: %v", rply)
}
}
func TestNMSliceRemove(t *testing.T) {
nm := &NMSlice{
NewNMData("1001"),
NewNMData("1003"),
&NavigableMap2{"Field1": NewNMData("Val")},
&NMSlice{},
}
if err := nm.Remove(nil); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(PathItems{}); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(PathItems{{Field: "field"}}); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(PathItems{{Index: IntPointer(-1)}, {}}); err != ErrWrongPath {
t.Error(err)
}
expected := &NMSlice{
NewNMData("1001"),
NewNMData("1003"),
&NavigableMap2{"Field1": NewNMData("Val")},
}
if err := nm.Remove(PathItems{{Index: IntPointer(-1)}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if err := nm.Remove(PathItems{{Index: IntPointer(1)}, {}}); err != ErrWrongPath {
t.Error(err)
}
expected = &NMSlice{
NewNMData("1001"),
&NavigableMap2{"Field1": NewNMData("Val")},
}
if err := nm.Remove(PathItems{{Index: IntPointer(1)}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if err := nm.Remove(PathItems{{Index: IntPointer(10)}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
if err := nm.Remove(PathItems{{Index: IntPointer(1)}, {Field: "Field1", Index: IntPointer(1)}}); err != ErrWrongPath {
t.Error(err)
}
expected = &NMSlice{
NewNMData("1001"),
}
if err := nm.Remove(PathItems{{Index: IntPointer(1)}, {Field: "Field1"}}); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(nm, expected) {
t.Errorf("Expected %s ,received: %s", expected, nm)
}
}

View File

@@ -0,0 +1,184 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"net"
"strings"
)
// NewOrderedNavigableMap initializates a structure of OrderedNavigableMap with a NavigableMap2
func NewOrderedNavigableMap() *OrderedNavigableMap {
return &OrderedNavigableMap{
nm: NavigableMap2{},
orderIdx: NewPathItemList(),
orderRef: make(map[string][]*PathItemElement),
}
}
// OrderedNavigableMap is the same as NavigableMap2 but keeps the order of fields
type OrderedNavigableMap struct {
nm NMInterface
orderIdx *PathItemList
orderRef map[string][]*PathItemElement
}
// String returns the map as json string
func (onm *OrderedNavigableMap) String() string {
return onm.nm.String()
}
// GetFirstElement returns the first element from the order
func (onm *OrderedNavigableMap) GetFirstElement() *PathItemElement {
return onm.orderIdx.Front()
}
// Interface returns navigble map that's inside
func (onm *OrderedNavigableMap) Interface() interface{} {
return onm.nm
}
// Field returns the item on the given path
func (onm *OrderedNavigableMap) Field(fldPath PathItems) (val NMInterface, err error) {
return onm.nm.Field(fldPath)
}
// Type returns the type of the NM map
func (onm *OrderedNavigableMap) Type() NMType {
return onm.nm.Type()
}
// Empty returns true if the NM is empty(no data)
func (onm *OrderedNavigableMap) Empty() bool {
return onm.nm.Empty()
}
// Remove removes the item for the given path and updates the order
func (onm *OrderedNavigableMap) Remove(fullPath FullPath) (err error) {
path := stripIdxFromLastPathElm(fullPath.Path)
if path == EmptyString || fullPath.PathItems[len(fullPath.PathItems)-1].Index != nil {
return ErrWrongPath
}
if err = onm.nm.Remove(fullPath.PathItems); err != nil {
return
}
for idxPath, slcIdx := range onm.orderRef {
if !strings.HasPrefix(idxPath, path) {
continue
}
for _, el := range slcIdx {
onm.orderIdx.Remove(el)
}
delete(onm.orderRef, idxPath)
}
return
}
// Set sets the value at the given path
// this is the old to be capable of building the code without updating all the code
// will be replaced with Set2 after we decide that is the optimal solution
func (onm *OrderedNavigableMap) Set(fldPath PathItems, val NMInterface) (addedNew bool, err error) {
return onm.Set2(&FullPath{PathItems: fldPath, Path: fldPath.String()}, val)
}
// Set2 sets the value at the given path
// this used with full path and the processed path to not calculate them for every set
func (onm *OrderedNavigableMap) Set2(fullPath *FullPath, val NMInterface) (addedNew bool, err error) {
if len(fullPath.PathItems) == 0 {
return false, ErrWrongPath
}
if addedNew, err = onm.nm.Set(fullPath.PathItems, val); err != nil {
return
}
var pathItmsSet []PathItems // can be multiples if we need to inflate due to missing Index in slice set
var nonIndexedSlcPath bool
if val.Type() == NMSliceType && fullPath.PathItems[len(fullPath.PathItems)-1].Index == nil { // special case when we overwrite with a slice without specifying indexes
nonIndexedSlcPath = true
pathItmsSet = make([]PathItems, len(*val.(*NMSlice)))
for i := 0; i < val.Len(); i++ {
pathItms := fullPath.PathItems.Clone()
pathItms[len(pathItms)-1].Index = IntPointer(i)
pathItmsSet[i] = pathItms
}
} else {
pathItmsSet = []PathItems{fullPath.PathItems}
}
path := stripIdxFromLastPathElm(fullPath.Path)
if !addedNew && nonIndexedSlcPath { // cleanup old references since the value is being overwritten
for idxPath, slcIdx := range onm.orderRef {
if !strings.HasPrefix(idxPath, path) {
continue
}
for _, el := range slcIdx {
onm.orderIdx.Remove(el)
}
delete(onm.orderRef, idxPath)
}
}
_, hasRef := onm.orderRef[path]
for _, pathItms := range pathItmsSet {
if addedNew || !hasRef {
onm.orderRef[path] = append(onm.orderRef[path], onm.orderIdx.PushBack(pathItms))
} else {
onm.orderIdx.MoveToBack(onm.orderRef[path][len(onm.orderRef[path])-1])
}
}
return
}
// Len returns the lenght of the map
func (onm OrderedNavigableMap) Len() int {
return onm.nm.Len()
}
// FieldAsString returns thevalue from path as string
func (onm *OrderedNavigableMap) FieldAsString(fldPath []string) (str string, err error) {
var val NMInterface
val, err = onm.nm.Field(NewPathToItem(fldPath))
if err != nil {
return
}
return IfaceAsString(val.Interface()), nil
}
// FieldAsInterface returns the interface at the path
func (onm *OrderedNavigableMap) FieldAsInterface(fldPath []string) (str interface{}, err error) {
var val NMInterface
val, err = onm.nm.Field(NewPathToItem(fldPath))
if err != nil {
return
}
return val.Interface(), nil
}
// RemoteHost is part of dataStorage interface
func (OrderedNavigableMap) RemoteHost() net.Addr {
return LocalAddr()
}
// GetOrder returns the elements order as a slice
// use this only for testing
func (onm *OrderedNavigableMap) GetOrder() (order []PathItems) {
for el := onm.GetFirstElement(); el != nil; el = el.Next() {
order = append(order, el.Value)
}
return
}

View File

@@ -0,0 +1,885 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"math/rand"
"reflect"
"testing"
)
func TestOrderedNavigableMap(t *testing.T) {
onm := NewOrderedNavigableMap()
onm.Set(PathItems{{Field: "Field1"}}, NewNMData(10))
expOrder := []PathItems{
PathItems{{Field: "Field1"}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, ToJSON(onm.GetOrder()))
}
onm.Set(
PathItems{{Field: "Field2", Index: IntPointer(0)}},
NewNMData("1001"))
expOrder = []PathItems{
PathItems{{Field: "Field1"}},
PathItems{{Field: "Field2", Index: IntPointer(0)}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, ToJSON(onm.GetOrder()))
}
onm.Set(
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(0)}},
NewNMData(10))
expOrder = []PathItems{
PathItems{{Field: "Field1"}},
PathItems{{Field: "Field2", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(0)}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, ToJSON(onm.GetOrder()))
}
onm.Set(
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(1)}},
NewNMData(11))
expOrder = []PathItems{
PathItems{{Field: "Field1"}},
PathItems{{Field: "Field2", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(1)}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, ToJSON(onm.GetOrder()))
}
onm.Set(
PathItems{{Field: "Field2", Index: IntPointer(2)}},
NewNMData(111))
expOrder = []PathItems{
PathItems{{Field: "Field1"}},
PathItems{{Field: "Field2", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(1)}},
PathItems{{Field: "Field2", Index: IntPointer(2)}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, ToJSON(onm.GetOrder()))
}
onm.Set(
PathItems{
{Field: "Field3"},
{Field: "Field4"},
{Field: "Field5"}},
NewNMData(5))
expOrder = []PathItems{
PathItems{{Field: "Field1"}},
PathItems{{Field: "Field2", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(0)}},
PathItems{
{Field: "Field2", Index: IntPointer(1)},
{Field: "Account", Index: IntPointer(1)}},
PathItems{{Field: "Field2", Index: IntPointer(2)}},
PathItems{
{Field: "Field3"},
{Field: "Field4"},
{Field: "Field5"}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, ToJSON(onm.GetOrder()))
}
var expnm NMInterface = NavigableMap2{
"Field1": NewNMData(10),
"Field2": &NMSlice{
NewNMData("1001"),
NavigableMap2{
"Account": &NMSlice{NewNMData(10), NewNMData(11)},
},
NewNMData(111),
},
"Field3": NavigableMap2{
"Field4": NavigableMap2{
"Field5": NewNMData(5),
},
},
}
if onm.Empty() {
t.Error("Expected not empty type")
}
if onm.Type() != NMMapType {
t.Errorf("Expected %v ,received: %v", NMDataType, onm.Type())
}
if !reflect.DeepEqual(expnm, onm.nm) {
t.Errorf("Expected %s ,received: %s", expnm, onm.nm)
}
// sliceDeNM
exp := &NMSlice{NewNMData("500"), NewNMData("502")}
path := PathItems{{Field: "Field2"}}
if _, err := onm.Set(path, exp); err != nil {
t.Error(err)
}
path = PathItems{{Field: "Field2"}}
if val, err := onm.Field(path); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(val, exp) {
t.Errorf("Expected %q ,received: %q", exp, val.Interface())
}
expOrder = []PathItems{
PathItems{{Field: "Field1"}},
PathItems{
{Field: "Field3"},
{Field: "Field4"},
{Field: "Field5"}},
PathItems{{Field: "Field2", Index: IntPointer(0)}},
PathItems{{Field: "Field2", Index: IntPointer(1)}},
}
if !reflect.DeepEqual(expOrder, onm.GetOrder()) {
t.Errorf("Expected %s ,received: %s", expOrder, onm.GetOrder())
}
path = PathItems{{Field: "Field2", Index: IntPointer(0)}}
if val, err := onm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != "500" {
t.Errorf("Expected %q ,received: %q", "500", val.Interface())
}
expnm = NavigableMap2{
"Field1": NewNMData(10),
"Field3": NavigableMap2{
"Field4": NavigableMap2{
"Field5": NewNMData(5),
},
},
"Field2": &NMSlice{
NewNMData("500"),
NewNMData("502"),
},
}
if !reflect.DeepEqual(expnm, onm.nm) {
t.Errorf("Expected %s ,received: %s", expnm, onm.nm)
}
}
func TestOrderedNavigableMapString(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{"Field1": NewNMData("1001")}}
expected := `{"Field1":1001}`
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
nm = &OrderedNavigableMap{nm: NavigableMap2{}}
expected = `{}`
if rply := nm.String(); rply != expected {
t.Errorf("Expected %q ,received: %q", expected, rply)
}
}
func TestOrderedNavigableMapInterface(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{"Field1": NewNMData("1001"), "Field2": NewNMData("1003")}}
expected := NavigableMap2{"Field1": NewNMData("1001"), "Field2": NewNMData("1003")}
if rply := nm.Interface(); !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected %s ,received: %s", ToJSON(expected), ToJSON(rply))
}
}
func TestOrderedNavigableMapField(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{}}
if _, err := nm.Field(PathItems{{}}); err != ErrNotFound {
t.Error(err)
}
nm = &OrderedNavigableMap{nm: NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}}
if _, err := nm.Field(nil); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Field(PathItems{{Field: "NaN"}}); err != ErrNotFound {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "Field1"}}); err != nil {
t.Error(err)
} else if val.Interface() != "1001" {
t.Errorf("Expected %q ,received: %q", "1001", val.Interface())
}
if _, err := nm.Field(PathItems{{Field: "Field1", Index: IntPointer(0)}}); err != ErrNotFound {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "Field5", Index: IntPointer(0)}}); err != nil {
t.Error(err)
} else if val.Interface() != 10 {
t.Errorf("Expected %q ,received: %q", 10, val.Interface())
}
if _, err := nm.Field(PathItems{{Field: "Field3", Index: IntPointer(0)}}); err != ErrNotFound {
t.Error(err)
}
if val, err := nm.Field(PathItems{{Field: "Field3"}, {Field: "Field4"}}); err != nil {
t.Error(err)
} else if val.Interface() != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val.Interface())
}
}
func TestOrderedNavigableMapType(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{}}
if nm.Type() != NMMapType {
t.Errorf("Expected %v ,received: %v", NMMapType, nm.Type())
}
}
func TestOrderedNavigableMapEmpty(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{}}
if !nm.Empty() {
t.Error("Expected empty type")
}
nm = &OrderedNavigableMap{nm: NavigableMap2{"Field1": NewNMData("1001")}}
if nm.Empty() {
t.Error("Expected not empty type")
}
}
func TestOrderedNavigableMapLen(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{}}
if rply := nm.Len(); rply != 0 {
t.Errorf("Expected 0 ,received: %v", rply)
}
nm = &OrderedNavigableMap{nm: NavigableMap2{"Field1": NewNMData("1001")}}
if rply := nm.Len(); rply != 1 {
t.Errorf("Expected 1 ,received: %v", rply)
}
}
func TestOrderedNavigableMapGetSet(t *testing.T) {
nm := NewOrderedNavigableMap()
nm.Set2(&FullPath{
PathItems: PathItems{{Field: "Account", Index: IntPointer(0)}},
Path: "Account",
}, NewNMData(1001))
nm.Set2(&FullPath{
PathItems: PathItems{{Field: "Account", Index: IntPointer(1)}},
Path: "Account",
}, NewNMData("account_on_new_branch"))
expectedOrder := []PathItems{
{{Field: "Account", Index: IntPointer(0)}},
{{Field: "Account", Index: IntPointer(1)}},
}
if recivedOrder := nm.GetOrder(); !reflect.DeepEqual(expectedOrder, recivedOrder) {
t.Errorf("Expected %s ,received: %s", expectedOrder, recivedOrder)
}
nm = &OrderedNavigableMap{
nm: NavigableMap2{
"Field1": NewNMData(10),
"Field2": &NMSlice{
NewNMData("1001"),
NavigableMap2{
"Account": &NMSlice{NewNMData(10), NewNMData(11)},
},
},
"Field3": NavigableMap2{
"Field4": NavigableMap2{
"Field5": NewNMData(5),
},
},
},
orderIdx: NewPathItemList(),
orderRef: make(map[string][]*PathItemElement),
}
path := PathItems{{Field: "Field1"}}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != 10 {
t.Errorf("Expected %q ,received: %q", 10, val.Interface())
}
path = PathItems{{Field: "Field3"}, {Field: "Field4"}, {Field: "Field5"}}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != 5 {
t.Errorf("Expected %q ,received: %q", 5, val.Interface())
}
path = PathItems{{Field: "Field2", Index: IntPointer(2)}}
if _, err := nm.Set(path, NewNMData("500")); err != nil {
t.Error(err)
}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != "500" {
t.Errorf("Expected %q ,received: %q", "500", val.Interface())
}
path = PathItems{{Field: "Field2", Index: IntPointer(1)}, {Field: "Account"}}
if _, err := nm.Set(path, NewNMData("5")); err != nil {
t.Error(err)
}
path = PathItems{{Field: "Field2", Index: IntPointer(1)}, {Field: "Account"}}
if val, err := nm.Field(path); err != nil {
t.Error(err)
} else if val.Interface() != "5" {
t.Errorf("Expected %q ,received: %q", "5", val.Interface())
}
path = PathItems{{Field: "Field2", Index: IntPointer(1)}, {Field: "Account", Index: IntPointer(0)}}
if _, err := nm.Field(path); err != ErrNotFound {
t.Error(err)
}
}
func TestOrderedNavigableMapFieldAsInterface(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}}
if _, err := nm.FieldAsInterface(nil); err != ErrWrongPath {
t.Error(err)
}
if val, err := nm.FieldAsInterface([]string{"Field3", "Field4"}); err != nil {
t.Error(err)
} else if val != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val)
}
if val, err := nm.FieldAsInterface([]string{"Field5[0]"}); err != nil {
t.Error(err)
} else if val != 10 {
t.Errorf("Expected %q ,received: %q", 10, val)
}
}
func TestOrderedNavigableMapFieldAsString(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}}
if _, err := nm.FieldAsString(nil); err != ErrWrongPath {
t.Error(err)
}
if val, err := nm.FieldAsString([]string{"Field3", "Field4"}); err != nil {
t.Error(err)
} else if val != "Val" {
t.Errorf("Expected %q ,received: %q", "Val", val)
}
if val, err := nm.FieldAsString([]string{"Field5[0]"}); err != nil {
t.Error(err)
} else if val != "10" {
t.Errorf("Expected %q ,received: %q", 10, val)
}
}
func TestOrderedNavigableMapGetOrder(t *testing.T) {
nm := NewOrderedNavigableMap()
nm.Set(PathItems{{Field: "Field1"}, {Field: "Field2", Index: IntPointer(0)}}, NewNMData("1003"))
nm.Set(PathItems{{Field: "Field1"}, {Field: "Field2", Index: IntPointer(1)}}, NewNMData("Val"))
nm.Set(PathItems{{Field: "Field3"}, {Field: "Field4"}, {Field: "Field5", Index: IntPointer(0)}}, NewNMData("1001"))
nm.Set(PathItems{{Field: "Field1"}, {Field: "Field2", Index: IntPointer(2)}}, NewNMData(101))
expected := []PathItems{
{{Field: "Field1"}, {Field: "Field2", Index: IntPointer(0)}},
{{Field: "Field1"}, {Field: "Field2", Index: IntPointer(1)}},
{{Field: "Field3"}, {Field: "Field4"}, {Field: "Field5", Index: IntPointer(0)}},
{{Field: "Field1"}, {Field: "Field2", Index: IntPointer(2)}},
}
if rply := nm.GetOrder(); !reflect.DeepEqual(rply, expected) {
t.Errorf("Expected %s ,received: %s", expected, rply)
}
}
//////////////////////////////////////////
func TestOrderedNavigableMapSet(t *testing.T) {
nm := NewOrderedNavigableMap()
if _, err := nm.Set(nil, nil); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(10)}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(10)}, {Field: "Field2"}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
path := PathItems{{Field: "Field1", Index: IntPointer(0)}}
if addedNew, err := nm.Set(path, NewNMData("1001")); err != nil {
t.Error(err)
} else if !addedNew {
t.Error("Expected the field to be added new")
}
nMap := NavigableMap2{"Field1": &NMSlice{NewNMData("1001")}}
order := []PathItems{path}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(0)}, {}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(10)}}, NewNMData("1001")); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(0)}, {}}, &NMSlice{}); err != ErrWrongPath {
t.Error(err)
}
if _, err := nm.Set(PathItems{{Field: "Field1", Index: IntPointer(10)}}, &NMSlice{}); err != ErrWrongPath {
t.Error(err)
}
nMap = NavigableMap2{"Field1": &NMSlice{NewNMData("1002")}}
order = []PathItems{path}
if addedNew, err := nm.Set(path, NewNMData("1002")); err != nil {
t.Error(err)
} else if addedNew {
t.Error("Expected the field to be only updated")
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
path = PathItems{{Field: "Field2"}}
nMap = NavigableMap2{
"Field1": &NMSlice{NewNMData("1002")},
"Field2": NewNMData("1002"),
}
order = append(order, path)
if addedNew, err := nm.Set(path, NewNMData("1002")); err != nil {
t.Error(err)
} else if !addedNew {
t.Error("Expected the field to be added new")
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
path = PathItems{{Field: "Field1", Index: IntPointer(1)}}
nMap = NavigableMap2{
"Field1": &NMSlice{NewNMData("1002"), NewNMData("1003")},
"Field2": NewNMData("1002"),
}
order = append(order, path)
if addedNew, err := nm.Set(path, NewNMData("1003")); err != nil {
t.Error(err)
} else if !addedNew {
t.Error("Expected the field to be added new")
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
path = PathItems{{Field: "Field3"}}
obj := &NMSlice{NewNMData("1004"), NewNMData("1005")}
nMap = NavigableMap2{
"Field1": &NMSlice{NewNMData("1002"), NewNMData("1003")},
"Field2": NewNMData("1002"),
"Field3": obj,
}
order = append(order, PathItems{{Field: "Field3", Index: IntPointer(0)}}, PathItems{{Field: "Field3", Index: IntPointer(1)}})
if addedNew, err := nm.Set(path, obj); err != nil {
t.Error(err)
} else if !addedNew {
t.Error("Expected the field to be added new")
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
obj = &NMSlice{NewNMData("1005"), NewNMData("1006")}
nMap = NavigableMap2{
"Field1": &NMSlice{NewNMData("1005"), NewNMData("1006")},
"Field2": NewNMData("1002"),
"Field3": &NMSlice{NewNMData("1004"), NewNMData("1005")},
}
order = []PathItems{
{{Field: "Field2"}},
{{Field: "Field3", Index: IntPointer(0)}},
{{Field: "Field3", Index: IntPointer(1)}},
{{Field: "Field1", Index: IntPointer(0)}},
{{Field: "Field1", Index: IntPointer(1)}},
}
if addedNew, err := nm.Set(PathItems{{Field: "Field1"}}, obj); err != nil {
t.Error(err)
} else if addedNew {
t.Error("Expected the field to be only updated")
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
obj = &NMSlice{NewNMData("1005"), NewNMData("1006")}
nMap = NavigableMap2{
"Field1": &NMSlice{NewNMData("1005"), NewNMData("1006")},
"Field2": NewNMData("1002"),
"Field3": &NMSlice{NewNMData("1004"), NewNMData("1007")},
}
order = []PathItems{
{{Field: "Field2"}},
{{Field: "Field3", Index: IntPointer(0)}},
{{Field: "Field1", Index: IntPointer(0)}},
{{Field: "Field1", Index: IntPointer(1)}},
{{Field: "Field3", Index: IntPointer(1)}},
}
if addedNew, err := nm.Set(PathItems{{Field: "Field3", Index: IntPointer(-1)}}, NewNMData("1007")); err != nil {
t.Error(err)
} else if addedNew {
t.Error("Expected the field to be only updated")
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
}
func TestOrderedNavigableMapRemove(t *testing.T) {
nm := NewOrderedNavigableMap()
nm.Set(PathItems{{Field: "Field2"}}, NewNMData("1003"))
nm.Set(PathItems{{Field: "Field3"}, {Field: "Field4"}}, NewNMData("Val"))
nm.Set(PathItems{{Field: "Field1"}}, NewNMData("1001"))
nm.Set(PathItems{{Field: "Field5"}}, &NMSlice{NewNMData(10), NewNMData(101)})
if err := nm.Remove(FullPath{}); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(FullPath{PathItems: PathItems{}}); err != ErrWrongPath {
t.Error(err)
}
if err := nm.Remove(FullPath{PathItems: PathItems{{Field: "field"}}, Path: "field"}); err != nil {
t.Error(err)
}
if err := nm.Remove(FullPath{PathItems: PathItems{{Index: IntPointer(-1)}, {}}}); err != ErrWrongPath {
t.Error(err)
}
nMap := NavigableMap2{
"Field1": NewNMData("1001"),
"Field2": NewNMData("1003"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
order := []PathItems{
{{Field: "Field2"}},
{{Field: "Field3"}, {Field: "Field4"}},
{{Field: "Field1"}},
{{Field: "Field5", Index: IntPointer(0)}},
{{Field: "Field5", Index: IntPointer(1)}},
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
nMap = NavigableMap2{
"Field1": NewNMData("1001"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
"Field5": &NMSlice{NewNMData(10), NewNMData(101)},
}
order = []PathItems{
{{Field: "Field3"}, {Field: "Field4"}},
{{Field: "Field1"}},
{{Field: "Field5", Index: IntPointer(0)}},
{{Field: "Field5", Index: IntPointer(1)}},
}
if err := nm.Remove(FullPath{PathItems: PathItems{{Field: "Field2"}}, Path: "Field2"}); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
nMap = NavigableMap2{
"Field1": NewNMData("1001"),
"Field3": NavigableMap2{"Field4": NewNMData("Val")},
}
order = []PathItems{
{{Field: "Field3"}, {Field: "Field4"}},
{{Field: "Field1"}},
}
if err := nm.Remove(FullPath{PathItems: PathItems{{Field: "Field5"}}, Path: "Field5"}); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(nm.nm, nMap) {
t.Errorf("Expected %s ,received: %s", nMap, nm)
}
if !reflect.DeepEqual(nm.GetOrder(), order) {
t.Errorf("Expected %s ,received: %s", order, nm.GetOrder())
}
if err := nm.Remove(FullPath{PathItems: PathItems{{Field: "Field1", Index: IntPointer(0)}, {}}}); err != ErrWrongPath {
t.Error(err)
}
}
func TestOrderedNavigableRemote(t *testing.T) {
nm := &OrderedNavigableMap{nm: NavigableMap2{"Field1": NewNMData("1001")}}
eOut := LocalAddr()
if rcv := nm.RemoteHost(); !reflect.DeepEqual(eOut, rcv) {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
}
var generator = rand.New(rand.NewSource(42))
var gen = generateRandomTemplate(10_000)
type benchData struct {
path []string
pathItems PathItems
strPath string
data string
}
func generateRandomPath() (out []string) {
size := generator.Intn(16) + 1
out = make([]string, size)
for i := 0; i < size; i++ {
out[i] = Sha1(GenUUID())
}
return
}
func generateRandomTemplate(size int) (out []benchData) {
out = make([]benchData, size)
for i := 0; i < size; i++ {
out[i].path = generateRandomPath()
out[i].data = UUIDSha1Prefix()
out[i].pathItems = NewPathToItem(out[i].path)
out[i].strPath = out[i].pathItems.String()
// out[i].pathItems[len(out[i].pathItems)-1].Index = IntPointer(0)
}
return
}
func BenchmarkOrderdNavigableMapSet2(b *testing.B) {
nm := NewOrderedNavigableMap()
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, data := range gen {
if _, err := nm.Set2(&FullPath{PathItems: data.pathItems, Path: data.strPath}, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
}
}
/*
func BenchmarkNavigableMapOld1Set(b *testing.B) {
nm := NewNavigableMapOld1(nil)
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, data := range gen {
nm.Set(data.path, data.data, false)
}
}
}
func BenchmarkOrderdNavigableMapSet(b *testing.B) {
nm := NewOrderedNavigableMap()
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, data := range gen {
if _,err := nm.Set(data.pathItems, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
}
}
func BenchmarkNavigableMapSet(b *testing.B) {
nm := NavigableMap2{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, data := range gen {
if _,err := nm.Set(data.pathItems, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
}
}
func BenchmarkNavigableMapOldSet(b *testing.B) {
nm := NavigableMap{}
b.ResetTimer()
for n := 0; n < b.N; n++ {
for _, data := range gen {
if _,err := nm.Set(data.path, data.data); err != nil {
b.Log(err, data.path)
}
}
}
}
func BenchmarkOrderdNavigableMapFieldAsInterface(b *testing.B) {
nm := NewOrderedNavigableMap()
for _, data := range gen {
if _,err := nm.Set(data.pathItems, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, data := range gen {
if val, err := nm.FieldAsInterface(data.path); err != nil {
b.Log(err)
} else if (*(val.(*NMSlice)))[0].Interface() != data.data {
b.Errorf("Expected %q ,received: %q", data.data, val)
}
}
}
}
func BenchmarkNavigableMapFieldAsInterface(b *testing.B) {
nm := NavigableMap2{}
for _, data := range gen {
if _,err := nm.Set(data.pathItems, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, data := range gen {
if val, err := nm.FieldAsInterface(data.path); err != nil {
b.Log(err)
} else if (*(val.(*NMSlice)))[0].Interface() != data.data {
b.Errorf("Expected %q ,received: %q", data.data, val)
}
}
}
}
func BenchmarkNavigableMapOldFieldAsInterface(b *testing.B) {
nm := NavigableMap{}
for _, data := range gen {
if _,err := nm.Set(data.path, data.data); err != nil {
b.Log(err, data.path)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, data := range gen {
if val, err := nm.FieldAsInterface(data.path); err != nil {
b.Log(err)
} else if val != data.data {
b.Errorf("Expected %q ,received: %q", data.data, val)
}
}
}
}
func BenchmarkNavigableMapOld1FieldAsInterface(b *testing.B) {
nm := NewNavigableMapOld1(nil)
for _, data := range gen {
nm.Set(data.path, data.data, true)
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, data := range gen {
if val, err := nm.FieldAsInterface(data.path); err != nil {
b.Log(err)
} else if val != data.data {
b.Errorf("Expected %q ,received: %q", data.data, val)
}
}
}
}
func BenchmarkOrderdNavigableMapField(b *testing.B) {
nm := NewOrderedNavigableMap()
for _, data := range gen {
if _,err := nm.Set(data.pathItems, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, data := range gen {
if val, err := nm.Field(data.pathItems); err != nil {
b.Log(err)
} else if val.Interface() != data.data {
b.Errorf("Expected %q ,received: %q", data.data, val.Interface())
}
}
}
}
func BenchmarkNavigableMapField(b *testing.B) {
nm := NavigableMap2{}
for _, data := range gen {
if _,err := nm.Set(data.pathItems, NewNMData(data.data)); err != nil {
b.Log(err, data.path)
}
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, data := range gen {
if val, err := nm.Field(data.pathItems); err != nil {
b.Log(err)
} else if val.Interface() != data.data {
b.Errorf("Expected %q ,received: %q", data.data, val.Interface())
}
}
}
}
//*/

149
utils/pathitem.go Normal file
View File

@@ -0,0 +1,149 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"strconv"
"strings"
)
// stripIdxFromLastPathElm will remove the index from the last path element
func stripIdxFromLastPathElm(path string) string {
lastDotIdx := strings.LastIndexByte(path, '.')
lastIdxStart := strings.LastIndexByte(path, '[')
if lastIdxStart == -1 ||
(lastDotIdx != -1 && lastDotIdx > lastIdxStart) {
return path
}
return path[:lastIdxStart]
}
// FullPath is the path to the item with all the needed fields
type FullPath struct {
PathItems PathItems
Path string
}
// NewPathToItem returns the computed PathItems out of slice one
func NewPathToItem(path []string) (pItms PathItems) {
pItms = make(PathItems, len(path))
for i, v := range path {
field, indx := GetPathIndex(v)
pItms[i] = PathItem{
Field: field,
Index: indx,
}
}
return
}
// PathItems a list of PathItem used to describe the path to an item from a NavigableMap
type PathItems []PathItem
// Clone creates a copy
func (path PathItems) Clone() (c PathItems) {
if path == nil {
return
}
c = make(PathItems, len(path))
for i, v := range path {
c[i] = v.Clone()
}
return
}
func (path PathItems) String() (out string) {
for _, v := range path {
out += NestingSep + v.String()
}
if out == "" {
return
}
return out[1:]
}
// PathItem used by the NM interface to store the path information
type PathItem struct {
Field string
Index *int
}
// Equal returns true if p==p2
func (p PathItem) Equal(p2 PathItem) bool {
if p.Field != p2.Field {
return false
}
if p.Index == nil && p2.Index == nil {
return true
}
if p.Index != nil && p2.Index != nil {
return *p.Index == *p2.Index
}
return false
}
func (p PathItem) String() (out string) {
out = p.Field
if p.Index != nil {
out += IdxStart + strconv.Itoa(*p.Index) + IdxEnd
}
return
}
// Clone creates a copy
func (p PathItem) Clone() (c PathItem) {
// if p == nil {
// return
// }
c.Field = p.Field
if p.Index != nil {
c.Index = IntPointer(*p.Index)
}
return
}
// GetPathIndex returns the path and index if index present
// path[index]=>path,index
// path=>path,nil
func GetPathIndex(spath string) (opath string, idx *int) {
idxStart := strings.Index(spath, IdxStart)
if idxStart == -1 || !strings.HasSuffix(spath, IdxEnd) {
return spath, nil
}
slctr := spath[idxStart+1 : len(spath)-1]
opath = spath[:idxStart]
// if strings.HasPrefix(slctr, DynamicDataPrefix) {
// return
// }
idxVal, err := strconv.Atoi(slctr)
if err != nil {
return spath, nil
}
return opath, &idxVal
}
func GetPathWithoutIndex(spath string) (opath string) {
idxStart := strings.LastIndex(spath, IdxStart)
if idxStart == -1 || !strings.HasSuffix(spath, IdxEnd) {
return spath
}
opath = spath[:idxStart]
return
}

147
utils/pathitem_test.go Normal file
View File

@@ -0,0 +1,147 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
import (
"reflect"
"strings"
"testing"
)
func TestStripIdxFromLastPathElm(t *testing.T) {
str := ""
if strp := stripIdxFromLastPathElm(str); strp != "" {
t.Errorf("received: <%s>", strp)
}
str = "mypath[0]"
if strp := stripIdxFromLastPathElm(str); strp != "mypath" {
t.Errorf("received: <%s>", strp)
}
str = "mypath"
if strp := stripIdxFromLastPathElm(str); strp != "mypath" {
t.Errorf("received: <%s>", strp)
}
str = "mypath.mypath2[0]"
if strp := stripIdxFromLastPathElm(str); strp != "mypath.mypath2" {
t.Errorf("received: <%s>", strp)
}
str = "mypath.mypath2"
if strp := stripIdxFromLastPathElm(str); strp != "mypath.mypath2" {
t.Errorf("received: <%s>", strp)
}
str = "mypath[1].mypath2[0]"
if strp := stripIdxFromLastPathElm(str); strp != "mypath[1].mypath2" {
t.Errorf("received: <%s>", strp)
}
str = "mypath[1].mypath2"
if strp := stripIdxFromLastPathElm(str); strp != "mypath[1].mypath2" {
t.Errorf("received: <%s>", strp)
}
}
func TestNewPathToItem(t *testing.T) {
pathSlice := strings.Split("*req.Field1[0].Account", NestingSep)
expected := PathItems{{Field: MetaReq}, {Field: "Field1", Index: IntPointer(0)}, {Field: Account}}
if rply := NewPathToItem(pathSlice); !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %s, received: %s", ToJSON(expected), ToJSON(rply))
}
pathSlice = []string{}
expected = PathItems{}
if rply := NewPathToItem(pathSlice); !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %s, received: %s", ToJSON(expected), ToJSON(rply))
}
}
func TestPathItemString(t *testing.T) {
path := PathItem{Field: MetaReq}
expected := MetaReq
if rply := path.String(); expected != rply {
t.Errorf("Expected: %q, received: %q", expected, rply)
}
path = PathItem{Field: MetaReq, Index: IntPointer(10)}
expected = MetaReq + "[10]"
if rply := path.String(); expected != rply {
t.Errorf("Expected: %q, received: %q", expected, rply)
}
}
func TestPathItemEqual(t *testing.T) {
path := PathItem{Field: MetaReq}
p1 := PathItem{Field: MetaReq}
if !path.Equal(p1) {
t.Errorf("Expected %s to be equal to %s", ToJSON(path), ToJSON(p1))
}
p1 = PathItem{Field: MetaRep}
if path.Equal(p1) {
t.Errorf("Expected %s to not be equal to %s", ToJSON(path), ToJSON(p1))
}
p1 = PathItem{Field: MetaReq, Index: IntPointer(0)}
if path.Equal(p1) {
t.Errorf("Expected %s to not be equal to %s", ToJSON(path), ToJSON(p1))
}
path = PathItem{Field: MetaReq, Index: IntPointer(0)}
if !path.Equal(p1) {
t.Errorf("Expected %s to be equal to %s", ToJSON(path), ToJSON(p1))
}
}
func TestPathItemClone(t *testing.T) {
path := PathItem{Field: MetaReq, Index: IntPointer(0)}
expected := PathItem{Field: MetaReq, Index: IntPointer(0)}
rply := path.Clone()
*path.Index = 1
if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %s, received: %s", ToJSON(expected), ToJSON(rply))
}
var path2 PathItem
expected = PathItem{}
rply = path2.Clone()
if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %s, received: %s", ToJSON(expected), ToJSON(rply))
}
}
func TestPathItemsString(t *testing.T) {
expected := "*req.Field1[0].Account"
path := NewPathToItem(strings.Split(expected, NestingSep))
if rply := path.String(); expected != rply {
t.Errorf("Expected: %q, received: %q", expected, rply)
}
expected = ""
path = nil
if rply := path.String(); expected != rply {
t.Errorf("Expected: %q, received: %q", expected, rply)
}
}
func TestPathItemsClone(t *testing.T) {
path := NewPathToItem(strings.Split("*req.Field1[0].Account", NestingSep))
expected := NewPathToItem(strings.Split("*req.Field1[0].Account", NestingSep))
rply := path.Clone()
path[0] = PathItem{}
if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %s, received: %s", ToJSON(expected), ToJSON(rply))
}
expected = nil
path = nil
rply = path.Clone()
if !reflect.DeepEqual(expected, rply) {
t.Errorf("Expected: %s, received: %s", ToJSON(expected), ToJSON(rply))
}
}

248
utils/pathitemlist.go Normal file
View File

@@ -0,0 +1,248 @@
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package utils
// PathItemElement is an element of a linked list.
// Inspired by Go's double linked list
type PathItemElement struct {
// Next and previous pointers in the doubly-linked list of elements.
// To simplify the implementation, internally a list l is implemented
// as a ring, such that &l.root is both the next element of the last
// list element (l.Back()) and the previous element of the first list
// element (l.Front()).
next, prev *PathItemElement
// The list to which this element belongs.
list *PathItemList
// The value stored with this element.
Value PathItems
}
// Next returns the next list element or nil.
func (e *PathItemElement) Next() *PathItemElement {
if p := e.next; e.list != nil && p != &e.list.root {
return p
}
return nil
}
// Prev returns the previous list element or nil.
func (e *PathItemElement) Prev() *PathItemElement {
if p := e.prev; e.list != nil && p != &e.list.root {
return p
}
return nil
}
// PathItemList represents a doubly linked list.
// The zero value for PathItemList is an empty list ready to use.
type PathItemList struct {
root PathItemElement // sentinel list element, only &root, root.prev, and root.next are used
len int // current list length excluding (this) sentinel element
}
// Init initializes or clears list l.
func (l *PathItemList) Init() *PathItemList {
l.root.next = &l.root
l.root.prev = &l.root
l.len = 0
return l
}
// NewPathItemList returns an initialized list.
func NewPathItemList() *PathItemList { return new(PathItemList).Init() }
// Len returns the number of elements of list l.
// The complexity is O(1).
func (l *PathItemList) Len() int { return l.len }
// Front returns the first element of list l or nil if the list is empty.
func (l *PathItemList) Front() *PathItemElement {
if l.len == 0 {
return nil
}
return l.root.next
}
// Back returns the last element of list l or nil if the list is empty.
func (l *PathItemList) Back() *PathItemElement {
if l.len == 0 {
return nil
}
return l.root.prev
}
// lazyInit lazily initializes a zero PathItemList value.
func (l *PathItemList) lazyInit() {
if l.root.next == nil {
l.Init()
}
}
// insert inserts e after at, increments l.len, and returns e.
func (l *PathItemList) insert(e, at *PathItemElement) *PathItemElement {
n := at.next
at.next = e
e.prev = at
e.next = n
n.prev = e
e.list = l
l.len++
return e
}
// insertValue is a convenience wrapper for insert(&PathItemElement{Value: v}, at).
func (l *PathItemList) insertValue(v PathItems, at *PathItemElement) *PathItemElement {
return l.insert(&PathItemElement{Value: v}, at)
}
// remove removes e from its list, decrements l.len, and returns e.
func (l *PathItemList) remove(e *PathItemElement) *PathItemElement {
e.prev.next = e.next
e.next.prev = e.prev
e.next = nil // avoid memory leaks
e.prev = nil // avoid memory leaks
e.list = nil
l.len--
return e
}
// move moves e to next to at and returns e.
func (l *PathItemList) move(e, at *PathItemElement) *PathItemElement {
if e == at {
return e
}
e.prev.next = e.next
e.next.prev = e.prev
n := at.next
at.next = e
e.prev = at
e.next = n
n.prev = e
return e
}
// Remove removes e from l if e is an element of list l.
// It returns the element value e.Value.
// The element must not be nil.
func (l *PathItemList) Remove(e *PathItemElement) PathItems {
if e.list == l {
// if e.list == l, l must have been initialized when e was inserted
// in l or l == nil (e is a zero PathItemElement) and l.remove will crash
l.remove(e)
}
return e.Value
}
// PushFront inserts a new element e with value v at the front of list l and returns e.
func (l *PathItemList) PushFront(v PathItems) *PathItemElement {
l.lazyInit()
return l.insertValue(v, &l.root)
}
// PushBack inserts a new element e with value v at the back of list l and returns e.
func (l *PathItemList) PushBack(v PathItems) *PathItemElement {
l.lazyInit()
return l.insertValue(v, l.root.prev)
}
// InsertBefore inserts a new element e with value v immediately before mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *PathItemList) InsertBefore(v PathItems, mark *PathItemElement) *PathItemElement {
if mark.list != l {
return nil
}
// see comment in PathItemList.Remove about initialization of l
return l.insertValue(v, mark.prev)
}
// InsertAfter inserts a new element e with value v immediately after mark and returns e.
// If mark is not an element of l, the list is not modified.
// The mark must not be nil.
func (l *PathItemList) InsertAfter(v PathItems, mark *PathItemElement) *PathItemElement {
if mark.list != l {
return nil
}
// see comment in PathItemList.Remove about initialization of l
return l.insertValue(v, mark)
}
// MoveToFront moves element e to the front of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *PathItemList) MoveToFront(e *PathItemElement) {
if e.list != l || l.root.next == e {
return
}
// see comment in PathItemList.Remove about initialization of l
l.move(e, &l.root)
}
// MoveToBack moves element e to the back of list l.
// If e is not an element of l, the list is not modified.
// The element must not be nil.
func (l *PathItemList) MoveToBack(e *PathItemElement) {
if e.list != l || l.root.prev == e {
return
}
// see comment in PathItemList.Remove about initialization of l
l.move(e, l.root.prev)
}
// MoveBefore moves element e to its new position before mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *PathItemList) MoveBefore(e, mark *PathItemElement) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark.prev)
}
// MoveAfter moves element e to its new position after mark.
// If e or mark is not an element of l, or e == mark, the list is not modified.
// The element and mark must not be nil.
func (l *PathItemList) MoveAfter(e, mark *PathItemElement) {
if e.list != l || e == mark || mark.list != l {
return
}
l.move(e, mark)
}
// PushBackList inserts a copy of an other list at the back of list l.
// The lists l and other may be the same. They must not be nil.
func (l *PathItemList) PushBackList(other *PathItemList) {
l.lazyInit()
for i, e := other.Len(), other.Front(); i > 0; i, e = i-1, e.Next() {
l.insertValue(e.Value, l.root.prev)
}
}
// PushFrontList inserts a copy of an other list at the front of list l.
// The lists l and other may be the same. They must not be nil.
func (l *PathItemList) PushFrontList(other *PathItemList) {
l.lazyInit()
for i, e := other.Len(), other.Back(); i > 0; i, e = i-1, e.Prev() {
l.insertValue(e.Value, &l.root)
}
}

View File

@@ -267,6 +267,8 @@ func IfaceAsString(fld interface{}) (out string) {
return value.String()
case string:
return value
case NMInterface:
return value.String()
default: // Maybe we are lucky and the value converts to string
return ToJSON(fld)
}

View File

@@ -22,6 +22,15 @@ import (
"testing"
)
func TestProcessReSearchReplaceNil(t *testing.T) {
rsr := &ReSearchReplace{SearchRegexp: nil, ReplaceTemplate: "0$1@$2"}
source := "<sip:+4986517174963@127.0.0.1;transport=tcp>"
expectOut := ""
if outStr := rsr.Process(source); outStr != expectOut {
t.Error("Unexpected output from SearchReplace: ", outStr)
}
}
func TestProcessReSearchReplace(t *testing.T) {
rsr := &ReSearchReplace{SearchRegexp: regexp.MustCompile(`sip:\+49(\d+)@(\d*\.\d*\.\d*\.\d*)`), ReplaceTemplate: "0$1@$2"}
source := "<sip:+4986517174963@127.0.0.1;transport=tcp>"

View File

@@ -19,67 +19,57 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package utils
// NewStringSet returns a new StringSet
func NewStringSet(dataSlice []string) (s *StringSet) {
s = &StringSet{data: make(map[string]struct{})}
func NewStringSet(dataSlice []string) (s StringSet) {
s = make(StringSet)
s.AddSlice(dataSlice)
return s
}
// StringSet will manage data within a set
type StringSet struct {
data map[string]struct{}
}
type StringSet map[string]struct{}
// Add adds a key in set
func (s *StringSet) Add(val string) {
s.data[val] = struct{}{}
func (s StringSet) Add(val string) {
s[val] = struct{}{}
}
// Remove removes a key from set
func (s *StringSet) Remove(val string) {
delete(s.data, val)
func (s StringSet) Remove(val string) {
delete(s, val)
}
// Has returns if the key is in set
func (s *StringSet) Has(val string) bool {
_, has := s.data[val]
func (s StringSet) Has(val string) bool {
_, has := s[val]
return has
}
// AddSlice adds all the element of a slice
func (s *StringSet) AddSlice(dataSlice []string) {
func (s StringSet) AddSlice(dataSlice []string) {
for _, val := range dataSlice {
s.Add(val)
}
}
// AsSlice returns the keys as string slice
func (s *StringSet) AsSlice() []string {
result := make([]string, len(s.data))
func (s StringSet) AsSlice() []string {
result := make([]string, len(s))
i := 0
for k := range s.data {
for k := range s {
result[i] = k
i++
}
return result
}
// Data exports the internal map, so we can benefit for example of key iteration
func (s *StringSet) Data() map[string]struct{} {
return s.data
}
// Size returns the size of the set
func (s *StringSet) Size() int {
if s == nil || s.data == nil {
return 0
}
return len(s.data)
func (s StringSet) Size() int {
return len(s)
}
// Intersect removes all key s2 do not have
func (s *StringSet) Intersect(s2 *StringSet) {
for k := range s.data {
func (s StringSet) Intersect(s2 StringSet) {
for k := range s {
if !s2.Has(k) {
s.Remove(k)
}

View File

@@ -26,7 +26,7 @@ import (
func TestNewStringSet(t *testing.T) {
input := []string{}
exp := &StringSet{data: make(map[string]struct{})}
exp := make(StringSet)
if rcv := NewStringSet(input); !reflect.DeepEqual(rcv, exp) {
t.Errorf("Expected: %+v, received: %+v", exp, rcv)
}
@@ -36,7 +36,7 @@ func TestNewStringSet(t *testing.T) {
t.Errorf("Expected: %+v, received: %+v", exp, rcv)
}
input = []string{"test1", "test2", "test3"}
exp = &StringSet{data: make(map[string]struct{})}
exp = make(StringSet)
exp.AddSlice(input)
if rcv := NewStringSet(input); !reflect.DeepEqual(rcv, exp) {
t.Errorf("Expected: %+v, received: %+v", exp, rcv)
@@ -44,10 +44,10 @@ func TestNewStringSet(t *testing.T) {
}
func TestAdd(t *testing.T) {
s := &StringSet{data: map[string]struct{}{}}
eOut := &StringSet{data: map[string]struct{}{
s := make(StringSet)
eOut := StringSet{
"test": struct{}{},
}}
}
if reflect.DeepEqual(eOut, s) {
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
}
@@ -58,10 +58,10 @@ func TestAdd(t *testing.T) {
}
func TestRemove(t *testing.T) {
eOut := &StringSet{data: map[string]struct{}{}}
s := &StringSet{data: map[string]struct{}{
eOut := make(StringSet)
s := StringSet{
"test": struct{}{},
}}
}
if reflect.DeepEqual(eOut, s) {
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
}
@@ -72,25 +72,25 @@ func TestRemove(t *testing.T) {
}
func TestHas(t *testing.T) {
s := &StringSet{}
s := StringSet{}
if s.Has("test") {
t.Error("Expecting: false, received: true")
}
s = &StringSet{data: map[string]struct{}{
s = StringSet{
"test": struct{}{},
}}
}
if !s.Has("test") {
t.Error("Expecting: true, received: false")
}
}
func TestAddSlice(t *testing.T) {
s := &StringSet{data: map[string]struct{}{
"test": struct{}{}}}
eOut := &StringSet{data: map[string]struct{}{
s := StringSet{
"test": struct{}{}}
eOut := StringSet{
"test": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}}
"test2": struct{}{}}
s.AddSlice([]string{"test1", "test2"})
if !reflect.DeepEqual(eOut, s) {
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
@@ -98,15 +98,15 @@ func TestAddSlice(t *testing.T) {
}
func TestAsSlice(t *testing.T) {
s := &StringSet{}
s := StringSet{}
eOut := make([]string, 0)
if rcv := s.AsSlice(); !reflect.DeepEqual(eOut, rcv) {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
s = &StringSet{data: map[string]struct{}{
s = StringSet{
"test": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}}
"test2": struct{}{}}
eOut = []string{"test", "test1", "test2"}
rcv := s.AsSlice()
sort.Strings(rcv)
@@ -115,60 +115,46 @@ func TestAsSlice(t *testing.T) {
}
}
func TestData(t *testing.T) {
s := &StringSet{data: map[string]struct{}{
"test": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}}
eOut := map[string]struct{}{
"test": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}
if rcv := s.Data(); !reflect.DeepEqual(eOut, rcv) {
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
}
}
func TestSize(t *testing.T) {
s := &StringSet{}
s := StringSet{}
if rcv := s.Size(); rcv != 0 {
t.Errorf("Expecting: 0, received %+v", rcv)
}
s = &StringSet{data: map[string]struct{}{
s = StringSet{
"test0": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}}
"test2": struct{}{}}
if rcv := s.Size(); rcv != 3 {
t.Errorf("Expecting: 3, received %+v", rcv)
}
}
func TestIntersect(t *testing.T) {
s1 := &StringSet{data: map[string]struct{}{
s1 := StringSet{
"test0": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}}
s2 := &StringSet{data: map[string]struct{}{
"test2": struct{}{}}
s2 := StringSet{
"test0": struct{}{},
"test2": struct{}{},
"test3": struct{}{}}}
eOut := &StringSet{data: map[string]struct{}{
"test3": struct{}{}}
eOut := StringSet{
"test0": struct{}{},
"test2": struct{}{}}}
"test2": struct{}{}}
s1.Intersect(s2)
if !reflect.DeepEqual(eOut, s1) {
t.Errorf("Expecting: %+v, received: %+v", eOut, s1)
}
s1 = &StringSet{data: map[string]struct{}{
s1 = StringSet{
"test0": struct{}{},
"test1": struct{}{},
"test2": struct{}{}}}
s2 = &StringSet{data: map[string]struct{}{
"test2": struct{}{}}
s2 = StringSet{
"test3": struct{}{},
"test4": struct{}{},
"test5": struct{}{}}}
"test5": struct{}{}}
s1.Intersect(s2)
eOut = &StringSet{data: map[string]struct{}{}}
eOut = make(StringSet)
if !reflect.DeepEqual(eOut, s1) {
t.Errorf("Expecting: %+v, received: %+v", eOut, s1)
}