2021-08-25 13:54:13 +02:00
|
|
|
package orderedmap
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"sort"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Pair struct {
|
|
|
|
key string
|
|
|
|
value interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (kv *Pair) Key() string {
|
|
|
|
return kv.key
|
|
|
|
}
|
|
|
|
|
|
|
|
func (kv *Pair) Value() interface{} {
|
|
|
|
return kv.value
|
|
|
|
}
|
|
|
|
|
|
|
|
type ByPair struct {
|
|
|
|
Pairs []*Pair
|
|
|
|
LessFunc func(a *Pair, j *Pair) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a ByPair) Len() int { return len(a.Pairs) }
|
|
|
|
func (a ByPair) Swap(i, j int) { a.Pairs[i], a.Pairs[j] = a.Pairs[j], a.Pairs[i] }
|
|
|
|
func (a ByPair) Less(i, j int) bool { return a.LessFunc(a.Pairs[i], a.Pairs[j]) }
|
|
|
|
|
|
|
|
type OrderedMap struct {
|
|
|
|
keys []string
|
|
|
|
values map[string]interface{}
|
|
|
|
escapeHTML bool
|
|
|
|
}
|
|
|
|
|
2021-09-21 03:15:23 +02:00
|
|
|
func New() OrderedMap {
|
2021-08-25 13:54:13 +02:00
|
|
|
o := OrderedMap{}
|
|
|
|
o.keys = []string{}
|
|
|
|
o.values = map[string]interface{}{}
|
|
|
|
o.escapeHTML = true
|
2021-09-21 03:15:23 +02:00
|
|
|
return o
|
2021-08-25 13:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
func (o *OrderedMap) SetEscapeHTML(on bool) {
|
|
|
|
o.escapeHTML = on
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *OrderedMap) Get(key string) (interface{}, bool) {
|
|
|
|
val, exists := o.values[key]
|
|
|
|
return val, exists
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *OrderedMap) Set(key string, value interface{}) {
|
|
|
|
_, exists := o.values[key]
|
|
|
|
if !exists {
|
|
|
|
o.keys = append(o.keys, key)
|
|
|
|
}
|
|
|
|
o.values[key] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *OrderedMap) Delete(key string) {
|
|
|
|
// check key is in use
|
|
|
|
_, ok := o.values[key]
|
|
|
|
if !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// remove from keys
|
|
|
|
for i, k := range o.keys {
|
|
|
|
if k == key {
|
|
|
|
o.keys = append(o.keys[:i], o.keys[i+1:]...)
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// remove from values
|
|
|
|
delete(o.values, key)
|
|
|
|
}
|
|
|
|
|
2021-09-23 13:31:01 +02:00
|
|
|
func (o *OrderedMap) Clear() { // delete whole orderdmap
|
|
|
|
for _, key := range o.Keys() {
|
|
|
|
o.Delete(key)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-25 13:54:13 +02:00
|
|
|
func (o *OrderedMap) Keys() []string {
|
2021-09-21 03:15:23 +02:00
|
|
|
ret := make([]string, len(o.keys))
|
|
|
|
for i, v := range o.keys {
|
|
|
|
ret[i] = v
|
|
|
|
}
|
|
|
|
return ret
|
2021-08-25 13:54:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// SortKeys Sort the map keys using your sort func
|
|
|
|
func (o *OrderedMap) SortKeys(sortFunc func(keys []string)) {
|
|
|
|
sortFunc(o.keys)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort Sort the map using your sort func
|
|
|
|
func (o *OrderedMap) Sort(lessFunc func(a *Pair, b *Pair) bool) {
|
|
|
|
pairs := make([]*Pair, len(o.keys))
|
|
|
|
for i, key := range o.keys {
|
|
|
|
pairs[i] = &Pair{key, o.values[key]}
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Sort(ByPair{pairs, lessFunc})
|
|
|
|
|
|
|
|
for i, pair := range pairs {
|
|
|
|
o.keys[i] = pair.key
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o *OrderedMap) UnmarshalJSON(b []byte) error {
|
|
|
|
if o.values == nil {
|
|
|
|
o.values = map[string]interface{}{}
|
|
|
|
}
|
|
|
|
err := json.Unmarshal(b, &o.values)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
dec := json.NewDecoder(bytes.NewReader(b))
|
|
|
|
if _, err = dec.Token(); err != nil { // skip '{'
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
o.keys = make([]string, 0, len(o.values))
|
|
|
|
return decodeOrderedMap(dec, o)
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeOrderedMap(dec *json.Decoder, o *OrderedMap) error {
|
|
|
|
hasKey := make(map[string]bool, len(o.values))
|
|
|
|
for {
|
|
|
|
token, err := dec.Token()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if delim, ok := token.(json.Delim); ok && delim == '}' {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
key := token.(string)
|
|
|
|
if hasKey[key] {
|
|
|
|
// duplicate key
|
|
|
|
for j, k := range o.keys {
|
|
|
|
if k == key {
|
|
|
|
copy(o.keys[j:], o.keys[j+1:])
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
o.keys[len(o.keys)-1] = key
|
|
|
|
} else {
|
|
|
|
hasKey[key] = true
|
|
|
|
o.keys = append(o.keys, key)
|
|
|
|
}
|
|
|
|
|
|
|
|
token, err = dec.Token()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if delim, ok := token.(json.Delim); ok {
|
|
|
|
switch delim {
|
|
|
|
case '{':
|
|
|
|
if values, ok := o.values[key].(map[string]interface{}); ok {
|
|
|
|
newMap := OrderedMap{
|
|
|
|
keys: make([]string, 0, len(values)),
|
|
|
|
values: values,
|
|
|
|
escapeHTML: o.escapeHTML,
|
|
|
|
}
|
|
|
|
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
o.values[key] = newMap
|
|
|
|
} else if oldMap, ok := o.values[key].(OrderedMap); ok {
|
|
|
|
newMap := OrderedMap{
|
|
|
|
keys: make([]string, 0, len(oldMap.values)),
|
|
|
|
values: oldMap.values,
|
|
|
|
escapeHTML: o.escapeHTML,
|
|
|
|
}
|
|
|
|
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
o.values[key] = newMap
|
|
|
|
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case '[':
|
|
|
|
if values, ok := o.values[key].([]interface{}); ok {
|
|
|
|
if err = decodeSlice(dec, values, o.escapeHTML); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err = decodeSlice(dec, []interface{}{}, o.escapeHTML); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func decodeSlice(dec *json.Decoder, s []interface{}, escapeHTML bool) error {
|
|
|
|
for index := 0; ; index++ {
|
|
|
|
token, err := dec.Token()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if delim, ok := token.(json.Delim); ok {
|
|
|
|
switch delim {
|
|
|
|
case '{':
|
|
|
|
if index < len(s) {
|
|
|
|
if values, ok := s[index].(map[string]interface{}); ok {
|
|
|
|
newMap := OrderedMap{
|
|
|
|
keys: make([]string, 0, len(values)),
|
|
|
|
values: values,
|
|
|
|
escapeHTML: escapeHTML,
|
|
|
|
}
|
|
|
|
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s[index] = newMap
|
|
|
|
} else if oldMap, ok := s[index].(OrderedMap); ok {
|
|
|
|
newMap := OrderedMap{
|
|
|
|
keys: make([]string, 0, len(oldMap.values)),
|
|
|
|
values: oldMap.values,
|
|
|
|
escapeHTML: escapeHTML,
|
|
|
|
}
|
|
|
|
if err = decodeOrderedMap(dec, &newMap); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
s[index] = newMap
|
|
|
|
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err = decodeOrderedMap(dec, &OrderedMap{}); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case '[':
|
|
|
|
if index < len(s) {
|
|
|
|
if values, ok := s[index].([]interface{}); ok {
|
|
|
|
if err = decodeSlice(dec, values, escapeHTML); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else if err = decodeSlice(dec, []interface{}{}, escapeHTML); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
case ']':
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (o OrderedMap) MarshalJSON() ([]byte, error) {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
buf.WriteByte('{')
|
|
|
|
encoder := json.NewEncoder(&buf)
|
|
|
|
encoder.SetEscapeHTML(o.escapeHTML)
|
|
|
|
for i, k := range o.keys {
|
|
|
|
if i > 0 {
|
|
|
|
buf.WriteByte(',')
|
|
|
|
}
|
|
|
|
// add key
|
|
|
|
if err := encoder.Encode(k); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
buf.WriteByte(':')
|
|
|
|
// add value
|
|
|
|
if err := encoder.Encode(o.values[k]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buf.WriteByte('}')
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
The MIT License (MIT)
|
|
|
|
|
|
|
|
Copyright (c) 2017 Ian Coleman
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
|
|
in the Software without restriction, including without limitation the rights
|
|
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
|
|
furnished to do so, Subject to the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
|
|
copies or Substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
|
SOFTWARE.
|
|
|
|
*/
|