Skip to content

Commit

Permalink
Extensions working
Browse files Browse the repository at this point in the history
  • Loading branch information
tkrajina committed Mar 9, 2021
1 parent f4b54b7 commit 357507d
Show file tree
Hide file tree
Showing 7 changed files with 192 additions and 50 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
tags
gpxgo
pkg/*
.vscode/*
.vscode/*
vendor/*
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ go 1.16

require (
github.com/joeshaw/gengen v0.0.0-20190604015154-c77d87825f5a
github.com/stretchr/testify v1.7.0 // indirect
github.com/stretchr/testify v1.7.0
)
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190603231351-8aaa1484dc10/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
2 changes: 1 addition & 1 deletion gpx/converters.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ func NewGPXAttributes(attrs []xml.Attr) GPXAttributes {
namespacesByUrls[attr.Value] = attr.Name.Local
}
}
//fmt.Printf("namespaces: %#v\n", namespacesByUrls)
fmt.Printf("namespaces: %#v\n", namespacesByUrls)

res := map[string]map[string]Attr{}
for _, attr := range attrs {
Expand Down
86 changes: 62 additions & 24 deletions gpx/gpx11.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ type Node struct {
Nodes []Node `xml:",any"`
}

func (n Node) debugXMLChunk() []byte {
byts, err := xml.MarshalIndent(n, "", " ")
if err != nil {
return []byte("???")
}
return byts
}

func (n Node) toTokens(prefix string) (tokens []xml.Token) {
var attrs []xml.Attr
for _, a := range n.Attrs {
Expand Down Expand Up @@ -59,7 +67,7 @@ func (n *Node) SetAttr(key, value string) {
}
n.Attrs = append(n.Attrs, xml.Attr{
Name: xml.Name{
Space: n.SpaceName(),
Space: n.SpaceNameURL(),
Local: key,
},
Value: value,
Expand All @@ -68,7 +76,6 @@ func (n *Node) SetAttr(key, value string) {

func (n *Node) GetNode(path0 string) (node *Node, found bool) {
for subn := range n.Nodes {
//fmt.Println("", n.Nodes[subn].LocalName(), "<->", path0)
if n.Nodes[subn].LocalName() == path0 {
node = &n.Nodes[subn]
found = true
Expand All @@ -78,67 +85,66 @@ func (n *Node) GetNode(path0 string) (node *Node, found bool) {
return
}

func (n *Node) GetOrCreateNode(path ...string) *Node {
func (n *Node) getOrCreateNode(path ...string) *Node {
if len(path) == 0 {
return n
}

namespace := n.SpaceName()
if strings.HasSuffix(path[0], ":") {
namespace = strings.Trim(path[0], ":")
if n.XMLName.Space != namespace {
n.XMLName.Space = namespace
}
path = path[1:]
}

path0, rest := path[0], path[1:]

subNode, found := n.GetNode(path0)
if !found {
n.Nodes = append(n.Nodes, Node{
XMLName: xml.Name{
Space: n.XMLName.Space,
Local: path0,
Space: namespace,
},
Attrs: []xml.Attr{},
Attrs: nil,
})
subNode = &n.Nodes[len(n.Nodes)-1]
subNode = &(n.Nodes[len(n.Nodes)-1])
}

return subNode.GetOrCreateNode(rest...)
return subNode.getOrCreateNode(rest...)
}

func (n Node) IsEmpty() bool { return len(n.Nodes) == 0 && len(n.Attrs) == 0 && len(n.Data) == 0 }
func (n Node) LocalName() string { return n.XMLName.Local }
func (n Node) SpaceName() string { return n.XMLName.Space }
func (n Node) IsEmpty() bool { return len(n.Nodes) == 0 && len(n.Attrs) == 0 && len(n.Data) == 0 }
func (n Node) LocalName() string { return n.XMLName.Local }
func (n Node) SpaceNameURL() string { return n.XMLName.Space }
func (n Node) GetAttrOrEmpty(attr string) string {
val, _ := n.GetAttr(attr)
return val
}

type Extension struct {
Node
// XMLName xml.Name
// Attrs []xml.Attr `xml:",any,attr"`
Nodes []Node `xml:",any"`

// Filled before deserializing:
globalNsAttrs map[string]Attr
}

var _ xml.Marshaler = Extension{}

func (ex Extension) debugXMLChunk() []byte {
byts, err := xml.MarshalIndent(ex, "", " ")
if err != nil {
return []byte("???")
}
return byts
}

func (ex Extension) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
if len(ex.Node.Nodes) == 0 {
if len(ex.Nodes) == 0 {
return nil
}

//fmt.Printf("global ns: %#v\n", ex.globalNsAttrs)
start = xml.StartElement{Name: xml.Name{Local: start.Name.Local}, Attr: nil}
tokens := []xml.Token{start}
for _, node := range ex.Nodes {
prefix := ""
for _, v := range ex.globalNsAttrs {
//fmt.Println("value=", v.Value, "space name=", node.SpaceName())
if node.SpaceName() == v.Value || node.SpaceName() == v.Name.Local {
if node.SpaceNameURL() == v.Value || node.SpaceNameURL() == v.Name.Local {
prefix = v.replacement
}
}
Expand All @@ -163,6 +169,38 @@ func (ex Extension) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return nil
}

func (ex *Extension) GetOrCreateNode(namespaceURL string, path ...string) *Node {
// TODO: Check is len(nodes) == 0
var subNode *Node
for n := range ex.Nodes {
if ex.Nodes[n].SpaceNameURL() == namespaceURL && ex.Nodes[n].LocalName() == path[0] {
subNode = &ex.Nodes[n]
break
}
}
if subNode == nil {
ex.Nodes = append(ex.Nodes, Node{
XMLName: xml.Name{
Space: namespaceURL,
Local: path[0],
},
})
subNode = &ex.Nodes[len(ex.Nodes)-1]
}
return subNode.getOrCreateNode(path[1:]...)
}

func (ex *Extension) GetNode(path0 string) (node *Node, found bool) {
for subn := range ex.Nodes {
if ex.Nodes[subn].LocalName() == path0 {
node = &ex.Nodes[subn]
found = true
return
}
}
return
}

/*
The GPX XML hierarchy:
Expand Down
146 changes: 125 additions & 21 deletions gpx/gpx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1512,14 +1512,100 @@ func TestExtensionWithoutNamespace(t *testing.T) {
}
}

func TestNodesSubnodesAndAttrs(t *testing.T) {
t.Parallel()

var node Node

assert.Equal(t, 0, len(node.Attrs))
node.SetAttr("xxx", "yyy")
assert.Equal(t, 1, len(node.Attrs))
{
val, found := node.GetAttr("xxx")
assert.True(t, found)
assert.Equal(t, "yyy", val)
}

assert.Equal(t, 0, len(node.Nodes))
node.getOrCreateNode("aaa").Data = "aaa data"
assert.Equal(t, 1, len(node.Nodes))
assert.Equal(t, 0, len(node.Nodes[0].Attrs))

assert.Equal(t, &node.Nodes[0], node.getOrCreateNode("aaa"))

fmt.Println(string(node.debugXMLChunk()))
node.getOrCreateNode("aaa").SetAttr("aaa", "bbb")
fmt.Println(string(node.debugXMLChunk()))
assert.Equal(t, 1, len(node.Nodes[0].Attrs))
assert.Equal(t, "aaa", node.Nodes[0].Attrs[0].Name.Local)
assert.Equal(t, "bbb", node.Nodes[0].Attrs[0].Value)

fmt.Println(string(node.debugXMLChunk()))
node.getOrCreateNode("aaa", "bbb").SetAttr("aaa", "bbb")
fmt.Println(string(node.debugXMLChunk()))
assert.Equal(t, 1, len(node.Nodes))
assert.Equal(t, 1, len(node.Nodes[0].Nodes))
assert.Equal(t, "aaa", node.Nodes[0].Nodes[0].Attrs[0].Name.Local)
assert.Equal(t, "bbb", node.Nodes[0].Nodes[0].Attrs[0].Value)
}

func TestExtensionNodesAndAttrs(t *testing.T) {
t.Parallel()

var ext Extension
assert.Equal(t, 0, len(ext.Nodes))
ext.GetOrCreateNode("", "aaa").Data = "aaa data"
assert.Equal(t, 1, len(ext.Nodes))
assert.Equal(t, 0, len(ext.Nodes[0].Attrs))
ext.GetOrCreateNode("", "aaa").SetAttr("aaa", "bbb")
assert.Equal(t, 1, len(ext.Nodes[0].Attrs))
assert.Equal(t, "aaa", ext.Nodes[0].Attrs[0].Name.Local)
assert.Equal(t, "bbb", ext.Nodes[0].Attrs[0].Value)

fmt.Println(string(ext.debugXMLChunk()))
ext.GetOrCreateNode("", "aaa", "bbb").SetAttr("aaa", "bbb")
fmt.Println(string(ext.debugXMLChunk()))

{
fmt.Println("a", string(ext.debugXMLChunk()))
n1 := ext.GetOrCreateNode("", "aaa", "bbb")
fmt.Println("b", string(ext.debugXMLChunk()))
n2 := &ext.Nodes[0].Nodes[0]
fmt.Println("c", string(ext.debugXMLChunk()))
assert.Equal(t, fmt.Sprintf("%p", n1), fmt.Sprintf("%p", n2))
}

assert.Equal(t, 1, len(ext.Nodes))
assert.Equal(t, 1, len(ext.Nodes[0].Nodes))
assert.Equal(t, "aaa", ext.Nodes[0].Nodes[0].Attrs[0].Name.Local)
assert.Equal(t, "bbb", ext.Nodes[0].Nodes[0].Attrs[0].Value)
}

func TestCreateExtensionWithoutNamespace(t *testing.T) {
t.Parallel()

var original GPX
original.Extensions.GetOrCreateNode("aaa", "bbb", "ccc").Data = "ccc data"
original.Extensions.GetOrCreateNode("aaa", "bbb").SetAttr("key", "value")
fmt.Println("1:", string(original.Extensions.debugXMLChunk()))
original.Extensions.GetOrCreateNode("", "aaa", "bbb", "ccc").Data = "ccc data"
fmt.Println("2:", string(original.Extensions.debugXMLChunk()))
assert.Equal(t, 1, len(original.Extensions.Nodes))
assert.Equal(t, "aaa", original.Extensions.Nodes[0].XMLName.Local)
assert.Equal(t, "bbb", original.Extensions.Nodes[0].Nodes[0].XMLName.Local)
assert.Equal(t, 0, len(original.Extensions.Nodes[0].Nodes[0].Attrs), "attrs=%#v", original.Extensions.Nodes[0].Nodes[0].Attrs)
original.Extensions.GetOrCreateNode("", "aaa", "bbb").SetAttr("key", "value")
fmt.Println("3:", string(original.Extensions.debugXMLChunk()))
assert.Equal(t, 1, len(original.Extensions.Nodes[0].Nodes[0].Attrs), "attrs=%#v", original.Extensions.Nodes[0].Nodes[0].Attrs)
if t.Failed() {
t.FailNow()
}

val, found := original.Extensions.GetOrCreateNode("aaa", "bbb").GetAttr("key")
assert.Equal(t, "aaa", original.Extensions.Nodes[0].XMLName.Local)
assert.Equal(t, "bbb", original.Extensions.Nodes[0].Nodes[0].XMLName.Local)
assert.Equal(t, 1, len(original.Extensions.Nodes[0].Nodes[0].Attrs), "attrs=%#v", original.Extensions.Nodes[0].Nodes[0].Attrs)
assert.Equal(t, "key", original.Extensions.Nodes[0].Nodes[0].Attrs[0].Name.Local)
assert.Equal(t, "value", original.Extensions.Nodes[0].Nodes[0].Attrs[0].Value)

val, found := original.Extensions.GetOrCreateNode("", "aaa", "bbb").GetAttr("key")
assert.True(t, found)
assert.Equal(t, "value", val)

Expand Down Expand Up @@ -1551,41 +1637,59 @@ func TestCreateExtensionWithNamespace(t *testing.T) {

var original GPX
original.RegisterNamespace("ext", "http://trla.baba.lan")
original.Extensions.GetOrCreateNode("ext:", "aaa", "bbb", "ccc").Data = "ccc data"
original.Extensions.GetOrCreateNode("http://trla.baba.lan", "aaa", "bbb", "ccc").Data = "ccc data"

assert.Equal(t, "http://trla.baba.lan", original.Attrs.Attributes["xmlns"]["ext"].Value)
assert.NotEmpty(t, original.Attrs.Attributes["xmlns"]["ext"].replacement)

original.Extensions.GetOrCreateNode("http://trla.baba.lan", "aaa", "bbb").SetAttr("key", "value")
val, found := original.Extensions.GetOrCreateNode("http://trla.baba.lan", "aaa", "bbb").GetAttr("key")
assert.True(t, found)
assert.Equal(t, "value", val)

reparsed, err := reparse(original)
assert.Nil(t, err)

for n, g := range []GPX{original, *reparsed} {
rereparsed, err := reparse(*reparsed)
assert.Nil(t, err)

fmt.Println(string(original.Extensions.debugXMLChunk()))
fmt.Println(string(reparsed.Extensions.debugXMLChunk()))
assert.Equal(t, original.Extensions.debugXMLChunk(), reparsed.Extensions.debugXMLChunk())
assert.Equal(t, original.Extensions, reparsed.Extensions)

assert.Equal(t, 1, len(original.Attrs.Attributes))
assert.Equal(t, len(original.Attrs.Attributes), len(reparsed.Attrs.Attributes))
assert.Equal(t, original.Attrs.Attributes["xmlns"]["ext"].Attr, reparsed.Attrs.Attributes["xmlns"]["ext"].Attr)

assert.Equal(t, 1, len(reparsed.Extensions.Nodes))
assert.Equal(t, len(original.Extensions.Nodes), len(reparsed.Extensions.Nodes))
// assert.Equal(t, original.Extensions.XMLName, reparsed.Extensions.XMLName)
assert.Equal(t, original.Extensions.Nodes[0], reparsed.Extensions.Nodes[0])
// assert.Equal(t, original.Extensions.Attrs, reparsed.Extensions.Attrs)
// assert.Equal(t, original.Extensions.Data, reparsed.Extensions.Data)
assert.Equal(t, original.Extensions, reparsed.Extensions)

if t.Failed() {
t.FailNow()
}

for n, g := range []GPX{original, *reparsed, *rereparsed} {
fmt.Printf("Test %d\n", n)
node, found := g.Extensions.GetNode("aaa")
assert.True(t, found)
assert.NotNil(t, node)
assert.Equal(t, "ext", node.SpaceName())
assert.Equal(t, "http://trla.baba.lan", node.SpaceNameURL())

assert.Equal(t, "ext", node.SpaceName())
assert.Equal(t, "ext", g.Extensions.SpaceName())
assert.Equal(t, "http://trla.baba.lan", node.SpaceNameURL())

node, found = node.GetNode("bbb")
assert.True(t, found)
assert.NotNil(t, node)

assert.Equal(t, "ext", node.SpaceName())
assert.Equal(t, "ext", g.Extensions.SpaceName())

g.Extensions.GetOrCreateNode("aaa", "bbb").SetAttr("key", "value")

assert.Equal(t, "ext", node.SpaceName())
assert.Equal(t, "ext", g.Extensions.SpaceName())

val, found := g.Extensions.GetOrCreateNode("aaa", "bbb").GetAttr("key")
assert.True(t, found)
assert.Equal(t, "value", val)
assert.Equal(t, "http://trla.baba.lan", node.SpaceNameURL())

assert.Equal(t, "ext", g.Extensions.SpaceName())
assert.Equal(t, "http://trla.baba.lan", node.SpaceNameURL())

byts, err := g.ToXml(ToXmlParams{Indent: true})
assert.Nil(t, err)
Expand All @@ -1609,7 +1713,7 @@ func TestCreateExtensionWithNamespace(t *testing.T) {
func TestGarminExtensions(t *testing.T) {
t.Parallel()

original, reparsed := loadAndReparseFile(t, "../test_files/TestCreateExtensionWithNamespace")
original, reparsed := loadAndReparseFile(t, "../test_files/gpx_with_garmin_extension.gpx")
if t.Failed() {
t.FailNow()
}
Expand Down
2 changes: 0 additions & 2 deletions gpx/xml.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ func ParseBytes(bytes []byte) (*GPX, error) {
return nil, err
}

// TODO Attrs
return convertFromGpx10Models(g), nil
} else if version == "1.1" {
g := &gpx11Gpx{}
Expand All @@ -181,7 +180,6 @@ func ParseBytes(bytes []byte) (*GPX, error) {
return nil, err
}

// TODO Attrs
return convertFromGpx11Models(g), nil
} else {
return nil, errors.New("Invalid version:" + version)
Expand Down

0 comments on commit 357507d

Please sign in to comment.