mirror of
https://github.com/cgrates/cgrates.git
synced 2026-02-11 18:16:24 +05:00
Added OrderedNavigableMap implementation
This commit is contained in:
committed by
Dan Christian Bogos
parent
a14dbaeb49
commit
80cdfe78c9
@@ -439,7 +439,6 @@ func NewKamDlgReply(kamEvData []byte) (rpl KamDlgReply, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (self *KamDlgReply) String() string {
|
||||
mrsh, _ := json.Marshal(self)
|
||||
return string(mrsh)
|
||||
func (kdr *KamDlgReply) String() string {
|
||||
return utils.ToJSON(kdr)
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ func (cfg *CGRConfig) checkConfigSanity() error {
|
||||
if cfg.cacheCfg[utils.CacheClosedSessions].Limit == 0 {
|
||||
return fmt.Errorf("<%s> %s needs to be != 0, received: %d", utils.CacheS, utils.CacheClosedSessions, cfg.cacheCfg[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)
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ type SessionSCfg struct {
|
||||
ClientProtocol float64
|
||||
ChannelSyncInterval time.Duration
|
||||
TerminateAttempts int
|
||||
AlterableFields *utils.StringSet
|
||||
AlterableFields utils.StringSet
|
||||
}
|
||||
|
||||
func (scfg *SessionSCfg) loadFromJsonCfg(jsnCfg *SessionSJsonCfg) (err error) {
|
||||
|
||||
@@ -1804,7 +1804,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
|
||||
|
||||
@@ -129,16 +129,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})
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -1081,7 +1081,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 {
|
||||
@@ -1158,7 +1158,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
|
||||
|
||||
@@ -307,7 +307,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
|
||||
}
|
||||
@@ -322,7 +322,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
|
||||
}
|
||||
|
||||
@@ -895,23 +895,3 @@ func CastRPCErr(err error) error {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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
123
utils/dataprovider.go
Normal 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
190
utils/dataprovider_test.go
Normal 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
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
199
utils/navigablemap.go
Normal file
199
utils/navigablemap.go
Normal file
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
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"
|
||||
)
|
||||
|
||||
// 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 + "}"
|
||||
}
|
||||
|
||||
// Interface returns itself
|
||||
func (nm NavigableMap2) Interface() interface{} {
|
||||
return nm
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
el, has := nm[path[0].Field]
|
||||
if !has {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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{}
|
||||
val, err = nm.FieldAsInterface(fldPath)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return IfaceAsString(val), nil
|
||||
}
|
||||
|
||||
// RemoteHost is part of dataStorage interface
|
||||
func (NavigableMap2) RemoteHost() net.Addr {
|
||||
return LocalAddr()
|
||||
}
|
||||
440
utils/navigablemap_test.go
Normal file
440
utils/navigablemap_test.go
Normal 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
70
utils/nmdata.go
Normal 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
109
utils/nmdata_test.go
Normal 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
140
utils/nmslice.go
Normal 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
206
utils/nmslice_test.go
Normal 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)
|
||||
}
|
||||
|
||||
}
|
||||
184
utils/orderednavigablemap.go
Normal file
184
utils/orderednavigablemap.go
Normal 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
|
||||
}
|
||||
885
utils/orderednavigablemap_test.go
Normal file
885
utils/orderednavigablemap_test.go
Normal 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
149
utils/pathitem.go
Normal 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
147
utils/pathitem_test.go
Normal 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
248
utils/pathitemlist.go
Normal 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)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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>"
|
||||
|
||||
44
utils/set.go
44
utils/set.go
@@ -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)
|
||||
}
|
||||
|
||||
161
utils/set_test.go
Normal file
161
utils/set_test.go
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
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"
|
||||
"sort"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNewStringSet(t *testing.T) {
|
||||
input := []string{}
|
||||
exp := make(StringSet)
|
||||
if rcv := NewStringSet(input); !reflect.DeepEqual(rcv, exp) {
|
||||
t.Errorf("Expected: %+v, received: %+v", exp, rcv)
|
||||
}
|
||||
input = []string{"test"}
|
||||
exp.AddSlice(input)
|
||||
if rcv := NewStringSet(input); !reflect.DeepEqual(rcv, exp) {
|
||||
t.Errorf("Expected: %+v, received: %+v", exp, rcv)
|
||||
}
|
||||
input = []string{"test1", "test2", "test3"}
|
||||
exp = make(StringSet)
|
||||
exp.AddSlice(input)
|
||||
if rcv := NewStringSet(input); !reflect.DeepEqual(rcv, exp) {
|
||||
t.Errorf("Expected: %+v, received: %+v", exp, rcv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
s := make(StringSet)
|
||||
eOut := StringSet{
|
||||
"test": struct{}{},
|
||||
}
|
||||
if reflect.DeepEqual(eOut, s) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
|
||||
}
|
||||
s.Add("test")
|
||||
if !reflect.DeepEqual(eOut, s) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
eOut := make(StringSet)
|
||||
s := StringSet{
|
||||
"test": struct{}{},
|
||||
}
|
||||
if reflect.DeepEqual(eOut, s) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
|
||||
}
|
||||
s.Remove("test")
|
||||
if !reflect.DeepEqual(eOut, s) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHas(t *testing.T) {
|
||||
s := StringSet{}
|
||||
if s.Has("test") {
|
||||
t.Error("Expecting: false, received: true")
|
||||
}
|
||||
s = StringSet{
|
||||
"test": struct{}{},
|
||||
}
|
||||
if !s.Has("test") {
|
||||
t.Error("Expecting: true, received: false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddSlice(t *testing.T) {
|
||||
s := StringSet{
|
||||
"test": struct{}{}}
|
||||
eOut := StringSet{
|
||||
"test": struct{}{},
|
||||
"test1": struct{}{},
|
||||
"test2": struct{}{}}
|
||||
s.AddSlice([]string{"test1", "test2"})
|
||||
if !reflect.DeepEqual(eOut, s) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAsSlice(t *testing.T) {
|
||||
s := StringSet{}
|
||||
eOut := make([]string, 0)
|
||||
if rcv := s.AsSlice(); !reflect.DeepEqual(eOut, rcv) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
|
||||
}
|
||||
s = StringSet{
|
||||
"test": struct{}{},
|
||||
"test1": struct{}{},
|
||||
"test2": struct{}{}}
|
||||
eOut = []string{"test", "test1", "test2"}
|
||||
rcv := s.AsSlice()
|
||||
sort.Strings(rcv)
|
||||
if !reflect.DeepEqual(eOut, rcv) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, rcv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSize(t *testing.T) {
|
||||
s := StringSet{}
|
||||
if rcv := s.Size(); rcv != 0 {
|
||||
t.Errorf("Expecting: 0, received %+v", rcv)
|
||||
}
|
||||
s = StringSet{
|
||||
"test0": struct{}{},
|
||||
"test1": struct{}{},
|
||||
"test2": struct{}{}}
|
||||
if rcv := s.Size(); rcv != 3 {
|
||||
t.Errorf("Expecting: 3, received %+v", rcv)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntersect(t *testing.T) {
|
||||
s1 := StringSet{
|
||||
"test0": struct{}{},
|
||||
"test1": struct{}{},
|
||||
"test2": struct{}{}}
|
||||
s2 := StringSet{
|
||||
"test0": struct{}{},
|
||||
"test2": struct{}{},
|
||||
"test3": struct{}{}}
|
||||
eOut := StringSet{
|
||||
"test0": struct{}{},
|
||||
"test2": struct{}{}}
|
||||
s1.Intersect(s2)
|
||||
if !reflect.DeepEqual(eOut, s1) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s1)
|
||||
}
|
||||
s1 = StringSet{
|
||||
"test0": struct{}{},
|
||||
"test1": struct{}{},
|
||||
"test2": struct{}{}}
|
||||
s2 = StringSet{
|
||||
"test3": struct{}{},
|
||||
"test4": struct{}{},
|
||||
"test5": struct{}{}}
|
||||
s1.Intersect(s2)
|
||||
eOut = make(StringSet)
|
||||
if !reflect.DeepEqual(eOut, s1) {
|
||||
t.Errorf("Expecting: %+v, received: %+v", eOut, s1)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user