mirror of https://github.com/antonmedv/fx
Separate jsonx package
parent
58b646c3f4
commit
1bb2803186
@ -0,0 +1,259 @@
|
||||
package jsonx
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
jsonpath "github.com/antonmedv/fx/path"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Prev, Next, End *Node
|
||||
directParent *Node
|
||||
indirectParent *Node
|
||||
Collapsed *Node
|
||||
Depth uint8
|
||||
Key []byte
|
||||
Value []byte
|
||||
Size int
|
||||
Chunk []byte
|
||||
ChunkEnd *Node
|
||||
Comma bool
|
||||
Index int
|
||||
}
|
||||
|
||||
// append ands a node as a child to the current node (body of {...} or [...]).
|
||||
func (n *Node) append(child *Node) {
|
||||
if n.End == nil {
|
||||
n.End = n
|
||||
}
|
||||
n.End.Next = child
|
||||
child.Prev = n.End
|
||||
if child.End == nil {
|
||||
n.End = child
|
||||
} else {
|
||||
n.End = child.End
|
||||
}
|
||||
}
|
||||
|
||||
// adjacent adds a node as a sibling to the current node ({}{}{} or [][][]).
|
||||
func (n *Node) adjacent(child *Node) {
|
||||
end := n.End
|
||||
if end == nil {
|
||||
end = n
|
||||
}
|
||||
end.Next = child
|
||||
child.Prev = end
|
||||
}
|
||||
|
||||
func (n *Node) insertChunk(chunk *Node) {
|
||||
if n.ChunkEnd == nil {
|
||||
n.insertAfter(chunk)
|
||||
} else {
|
||||
n.ChunkEnd.insertAfter(chunk)
|
||||
}
|
||||
n.ChunkEnd = chunk
|
||||
}
|
||||
|
||||
func (n *Node) insertAfter(child *Node) {
|
||||
if n.Next == nil {
|
||||
n.Next = child
|
||||
child.Prev = n
|
||||
} else {
|
||||
old := n.Next
|
||||
n.Next = child
|
||||
child.Prev = n
|
||||
child.Next = old
|
||||
old.Prev = child
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) dropChunks() {
|
||||
if n.ChunkEnd == nil {
|
||||
return
|
||||
}
|
||||
|
||||
n.Chunk = nil
|
||||
|
||||
n.Next = n.ChunkEnd.Next
|
||||
if n.Next != nil {
|
||||
n.Next.Prev = n
|
||||
}
|
||||
|
||||
n.ChunkEnd = nil
|
||||
}
|
||||
|
||||
func (n *Node) HasChildren() bool {
|
||||
return n.End != nil
|
||||
}
|
||||
|
||||
func (n *Node) Parent() *Node {
|
||||
if n.directParent == nil {
|
||||
return nil
|
||||
}
|
||||
parent := n.directParent
|
||||
if parent.indirectParent != nil {
|
||||
parent = parent.indirectParent
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
func (n *Node) IsCollapsed() bool {
|
||||
return n.Collapsed != nil
|
||||
}
|
||||
|
||||
func (n *Node) Collapse() *Node {
|
||||
if n.End != nil && !n.IsCollapsed() {
|
||||
n.Collapsed = n.Next
|
||||
n.Next = n.End.Next
|
||||
if n.Next != nil {
|
||||
n.Next.Prev = n
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *Node) CollapseRecursively() {
|
||||
var at *Node
|
||||
if n.IsCollapsed() {
|
||||
at = n.Collapsed
|
||||
} else {
|
||||
at = n.Next
|
||||
}
|
||||
for at != nil && at != n.End {
|
||||
if at.HasChildren() {
|
||||
at.CollapseRecursively()
|
||||
at.Collapse()
|
||||
}
|
||||
at = at.Next
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) Expand() {
|
||||
if n.IsCollapsed() {
|
||||
if n.Next != nil {
|
||||
n.Next.Prev = n.End
|
||||
}
|
||||
n.Next = n.Collapsed
|
||||
n.Collapsed = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) ExpandRecursively(level, maxLevel int) {
|
||||
if level >= maxLevel {
|
||||
return
|
||||
}
|
||||
if n.IsCollapsed() {
|
||||
n.Expand()
|
||||
}
|
||||
it := n.Next
|
||||
for it != nil && it != n.End {
|
||||
if it.HasChildren() {
|
||||
it.ExpandRecursively(level+1, maxLevel)
|
||||
it = it.End.Next
|
||||
} else {
|
||||
it = it.Next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) FindChildByKey(key string) *Node {
|
||||
it := n.Next
|
||||
for it != nil && it != n.End {
|
||||
if it.Key != nil {
|
||||
k, err := strconv.Unquote(string(it.Key))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if k == key {
|
||||
return it
|
||||
}
|
||||
}
|
||||
if it.ChunkEnd != nil {
|
||||
it = it.ChunkEnd.Next
|
||||
} else if it.End != nil {
|
||||
it = it.End.Next
|
||||
} else {
|
||||
it = it.Next
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Node) FindChildByIndex(index int) *Node {
|
||||
for at := n.Next; at != nil && at != n.End; {
|
||||
if at.Index == index {
|
||||
return at
|
||||
}
|
||||
if at.End != nil {
|
||||
at = at.End.Next
|
||||
} else {
|
||||
at = at.Next
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *Node) paths(prefix string, paths *[]string, nodes *[]*Node) {
|
||||
it := n.Next
|
||||
for it != nil && it != n.End {
|
||||
var path string
|
||||
|
||||
if it.Key != nil {
|
||||
quoted := string(it.Key)
|
||||
unquoted, err := strconv.Unquote(quoted)
|
||||
if err == nil && jsonpath.Identifier.MatchString(unquoted) {
|
||||
path = prefix + "." + unquoted
|
||||
} else {
|
||||
path = prefix + "[" + quoted + "]"
|
||||
}
|
||||
} else if it.Index >= 0 {
|
||||
path = prefix + "[" + strconv.Itoa(it.Index) + "]"
|
||||
}
|
||||
|
||||
*paths = append(*paths, path)
|
||||
*nodes = append(*nodes, it)
|
||||
|
||||
if it.HasChildren() {
|
||||
it.paths(path, paths, nodes)
|
||||
it = it.End.Next
|
||||
} else {
|
||||
it = it.Next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Node) Children() ([]string, []*Node) {
|
||||
if !n.HasChildren() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var paths []string
|
||||
var nodes []*Node
|
||||
|
||||
var it *Node
|
||||
if n.IsCollapsed() {
|
||||
it = n.Collapsed
|
||||
} else {
|
||||
it = n.Next
|
||||
}
|
||||
|
||||
for it != nil && it != n.End {
|
||||
if it.Key != nil {
|
||||
key := string(it.Key)
|
||||
unquoted, err := strconv.Unquote(key)
|
||||
if err == nil {
|
||||
key = unquoted
|
||||
}
|
||||
paths = append(paths, key)
|
||||
nodes = append(nodes, it)
|
||||
}
|
||||
|
||||
if it.HasChildren() {
|
||||
it = it.End.Next
|
||||
} else {
|
||||
it = it.Next
|
||||
}
|
||||
}
|
||||
|
||||
return paths, nodes
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package main
|
||||
package jsonx
|
||||
|
||||
import (
|
||||
"strings"
|
@ -0,0 +1,9 @@
|
||||
package jsonx
|
||||
|
||||
func isHexDigit(ch byte) bool {
|
||||
return (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')
|
||||
}
|
||||
|
||||
func isDigit(ch byte) bool {
|
||||
return ch >= '0' && ch <= '9'
|
||||
}
|
@ -1,259 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
jsonpath "github.com/antonmedv/fx/path"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
prev, next, end *node
|
||||
directParent *node
|
||||
indirectParent *node
|
||||
collapsed *node
|
||||
depth uint8
|
||||
key []byte
|
||||
value []byte
|
||||
size int
|
||||
chunk []byte
|
||||
chunkEnd *node
|
||||
comma bool
|
||||
index int
|
||||
}
|
||||
|
||||
// append ands a node as a child to the current node (body of {...} or [...]).
|
||||
func (n *node) append(child *node) {
|
||||
if n.end == nil {
|
||||
n.end = n
|
||||
}
|
||||
n.end.next = child
|
||||
child.prev = n.end
|
||||
if child.end == nil {
|
||||
n.end = child
|
||||
} else {
|
||||
n.end = child.end
|
||||
}
|
||||
}
|
||||
|
||||
// adjacent adds a node as a sibling to the current node ({}{}{} or [][][]).
|
||||
func (n *node) adjacent(child *node) {
|
||||
end := n.end
|
||||
if end == nil {
|
||||
end = n
|
||||
}
|
||||
end.next = child
|
||||
child.prev = end
|
||||
}
|
||||
|
||||
func (n *node) insertChunk(chunk *node) {
|
||||
if n.chunkEnd == nil {
|
||||
n.insertAfter(chunk)
|
||||
} else {
|
||||
n.chunkEnd.insertAfter(chunk)
|
||||
}
|
||||
n.chunkEnd = chunk
|
||||
}
|
||||
|
||||
func (n *node) insertAfter(child *node) {
|
||||
if n.next == nil {
|
||||
n.next = child
|
||||
child.prev = n
|
||||
} else {
|
||||
old := n.next
|
||||
n.next = child
|
||||
child.prev = n
|
||||
child.next = old
|
||||
old.prev = child
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) dropChunks() {
|
||||
if n.chunkEnd == nil {
|
||||
return
|
||||
}
|
||||
|
||||
n.chunk = nil
|
||||
|
||||
n.next = n.chunkEnd.next
|
||||
if n.next != nil {
|
||||
n.next.prev = n
|
||||
}
|
||||
|
||||
n.chunkEnd = nil
|
||||
}
|
||||
|
||||
func (n *node) hasChildren() bool {
|
||||
return n.end != nil
|
||||
}
|
||||
|
||||
func (n *node) parent() *node {
|
||||
if n.directParent == nil {
|
||||
return nil
|
||||
}
|
||||
parent := n.directParent
|
||||
if parent.indirectParent != nil {
|
||||
parent = parent.indirectParent
|
||||
}
|
||||
return parent
|
||||
}
|
||||
|
||||
func (n *node) isCollapsed() bool {
|
||||
return n.collapsed != nil
|
||||
}
|
||||
|
||||
func (n *node) collapse() *node {
|
||||
if n.end != nil && !n.isCollapsed() {
|
||||
n.collapsed = n.next
|
||||
n.next = n.end.next
|
||||
if n.next != nil {
|
||||
n.next.prev = n
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func (n *node) collapseRecursively() {
|
||||
var at *node
|
||||
if n.isCollapsed() {
|
||||
at = n.collapsed
|
||||
} else {
|
||||
at = n.next
|
||||
}
|
||||
for at != nil && at != n.end {
|
||||
if at.hasChildren() {
|
||||
at.collapseRecursively()
|
||||
at.collapse()
|
||||
}
|
||||
at = at.next
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) expand() {
|
||||
if n.isCollapsed() {
|
||||
if n.next != nil {
|
||||
n.next.prev = n.end
|
||||
}
|
||||
n.next = n.collapsed
|
||||
n.collapsed = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) expandRecursively(level, maxLevel int) {
|
||||
if level >= maxLevel {
|
||||
return
|
||||
}
|
||||
if n.isCollapsed() {
|
||||
n.expand()
|
||||
}
|
||||
it := n.next
|
||||
for it != nil && it != n.end {
|
||||
if it.hasChildren() {
|
||||
it.expandRecursively(level+1, maxLevel)
|
||||
it = it.end.next
|
||||
} else {
|
||||
it = it.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) findChildByKey(key string) *node {
|
||||
it := n.next
|
||||
for it != nil && it != n.end {
|
||||
if it.key != nil {
|
||||
k, err := strconv.Unquote(string(it.key))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if k == key {
|
||||
return it
|
||||
}
|
||||
}
|
||||
if it.chunkEnd != nil {
|
||||
it = it.chunkEnd.next
|
||||
} else if it.end != nil {
|
||||
it = it.end.next
|
||||
} else {
|
||||
it = it.next
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) findChildByIndex(index int) *node {
|
||||
for at := n.next; at != nil && at != n.end; {
|
||||
if at.index == index {
|
||||
return at
|
||||
}
|
||||
if at.end != nil {
|
||||
at = at.end.next
|
||||
} else {
|
||||
at = at.next
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *node) paths(prefix string, paths *[]string, nodes *[]*node) {
|
||||
it := n.next
|
||||
for it != nil && it != n.end {
|
||||
var path string
|
||||
|
||||
if it.key != nil {
|
||||
quoted := string(it.key)
|
||||
unquoted, err := strconv.Unquote(quoted)
|
||||
if err == nil && jsonpath.Identifier.MatchString(unquoted) {
|
||||
path = prefix + "." + unquoted
|
||||
} else {
|
||||
path = prefix + "[" + quoted + "]"
|
||||
}
|
||||
} else if it.index >= 0 {
|
||||
path = prefix + "[" + strconv.Itoa(it.index) + "]"
|
||||
}
|
||||
|
||||
*paths = append(*paths, path)
|
||||
*nodes = append(*nodes, it)
|
||||
|
||||
if it.hasChildren() {
|
||||
it.paths(path, paths, nodes)
|
||||
it = it.end.next
|
||||
} else {
|
||||
it = it.next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *node) children() ([]string, []*node) {
|
||||
if !n.hasChildren() {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var paths []string
|
||||
var nodes []*node
|
||||
|
||||
var it *node
|
||||
if n.isCollapsed() {
|
||||
it = n.collapsed
|
||||
} else {
|
||||
it = n.next
|
||||
}
|
||||
|
||||
for it != nil && it != n.end {
|
||||
if it.key != nil {
|
||||
key := string(it.key)
|
||||
unquoted, err := strconv.Unquote(key)
|
||||
if err == nil {
|
||||
key = unquoted
|
||||
}
|
||||
paths = append(paths, key)
|
||||
nodes = append(nodes, it)
|
||||
}
|
||||
|
||||
if it.hasChildren() {
|
||||
it = it.end.next
|
||||
} else {
|
||||
it = it.next
|
||||
}
|
||||
}
|
||||
|
||||
return paths, nodes
|
||||
}
|
Loading…
Reference in New Issue