Stanzas can now contain multiple nested (extended) elements.
--- a/roster.go Sat Jan 07 21:20:23 2012 -0700
+++ b/roster.go Sat Jan 07 22:22:18 2012 -0700
@@ -52,7 +52,7 @@
rosterUpdate := rosterClients[client.Uid].rosterUpdate
iq := &Iq{From: client.Jid.String(), Id: <- Id, Type: "get",
- Nested: RosterQuery{}}
+ Nested: []interface{}{RosterQuery{}}}
ch := make(chan os.Error)
f := func(st Stanza) bool {
defer close(ch)
@@ -60,8 +60,14 @@
ch <- iq.Error
return false
}
- rq, ok := st.GetNested().(*RosterQuery)
- if !ok {
+ var rq *RosterQuery
+ for _, ele := range(st.GetNested()) {
+ if q, ok := ele.(*RosterQuery) ; ok {
+ rq = q
+ break
+ }
+ }
+ if rq == nil {
ch <- os.NewError(fmt.Sprintf(
"Roster query result not query: %v", st))
return false
@@ -103,8 +109,14 @@
func maybeUpdateRoster(client *Client, st Stanza) {
rosterUpdate := rosterClients[client.Uid].rosterUpdate
- rq, ok := st.GetNested().(*RosterQuery)
- if st.GetName() == "iq" && st.GetType() == "set" && ok {
+ var rq *RosterQuery
+ for _, ele := range(st.GetNested()) {
+ if q, ok := ele.(*RosterQuery) ; ok {
+ rq = q
+ break
+ }
+ }
+ if st.GetName() == "iq" && st.GetType() == "set" && rq != nil {
for _, item := range(rq.Item) {
rosterUpdate <- item
}
--- a/roster_test.go Sat Jan 07 21:20:23 2012 -0700
+++ b/roster_test.go Sat Jan 07 22:22:18 2012 -0700
@@ -14,7 +14,8 @@
// This is mostly just tests of the roster data structures.
func TestRosterIqMarshal(t *testing.T) {
- iq := &Iq{From: "from", Lang: "en", Nested: RosterQuery{}}
+ iq := &Iq{From: "from", Lang: "en", Nested:
+ []interface{}{RosterQuery{}}}
exp := `<iq from="from" xml:lang="en"><query xmlns="` +
NsRoster + `"></query></iq>`
assertMarshal(t, exp, iq)
@@ -26,7 +27,8 @@
r := strings.NewReader(str)
var st Stanza = &Iq{}
xml.Unmarshal(r, st)
- err := parseExtended(st, newRosterQuery)
+ m := map[string] func(*xml.Name) interface{}{NsRoster: newRosterQuery}
+ err := parseExtended(st, m)
if err != nil {
t.Fatalf("parseExtended: %v", err)
}
@@ -37,7 +39,12 @@
if nested == nil {
t.Fatalf("nested nil")
}
- rq, ok := nested.(*RosterQuery)
+ if len(nested) != 1 {
+ t.Fatalf("wrong size nested(%d): %v", len(nested),
+ nested)
+ }
+ var rq *RosterQuery
+ rq, ok := nested[0].(*RosterQuery)
if !ok {
t.Fatalf("nested not RosterQuery: %v",
reflect.TypeOf(nested))
--- a/stream.go Sat Jan 07 21:20:23 2012 -0700
+++ b/stream.go Sat Jan 07 22:22:18 2012 -0700
@@ -145,21 +145,15 @@
break
}
- // If it's a Stanza, we check its "Any" element for a
- // namespace that's registered with one of our
- // extensions. If so, we need to re-unmarshal into an
- // object of the correct type.
- if st, ok := obj.(Stanza) ; ok && st.generic() != nil {
- name := st.generic().XMLName
- ns := name.Space
- con := extStanza[ns]
- if con != nil {
- err = parseExtended(st, con)
- if err != nil {
- log.Printf("ext unmarshal: %v",
- err)
- break
- }
+ // If it's a Stanza, we try to unmarshal its innerxml
+ // into objects of the appropriate respective
+ // types. This is specified by our extensions.
+ if st, ok := obj.(Stanza) ; ok {
+ err = parseExtended(st, extStanza)
+ if err != nil {
+ log.Printf("ext unmarshal: %v",
+ err)
+ break
}
}
@@ -168,35 +162,35 @@
}
}
-func parseExtended(st Stanza, con func(*xml.Name) interface{}) os.Error {
- name := st.generic().XMLName
- nested := con(&name)
-
+func parseExtended(st Stanza, extStanza map[string] func(*xml.Name) interface{}) os.Error {
// Now parse the stanza's innerxml to find the string that we
// can unmarshal this nested element from.
reader := strings.NewReader(st.innerxml())
p := xml.NewParser(reader)
- var start *xml.StartElement
for {
t, err := p.Token()
+ if err == os.EOF {
+ break
+ }
if err != nil {
return err
}
if se, ok := t.(xml.StartElement) ; ok {
- if se.Name.Space == name.Space {
- start = &se
- break
+ if con, ok := extStanza[se.Name.Space] ; ok {
+ // Call the indicated constructor.
+ nested := con(&se.Name)
+
+ // Unmarshal the nested element and
+ // stuff it back into the stanza.
+ err := p.Unmarshal(nested, &se)
+ if err != nil {
+ return err
+ }
+ st.addNested(nested)
}
}
}
- // Unmarshal the nested element and stuff it back into the
- // stanza.
- err := p.Unmarshal(nested, start)
- if err != nil {
- return err
- }
- st.setNested(nested)
return nil
}
@@ -594,15 +588,21 @@
if res != "" {
bindReq.Resource = &res
}
- msg := &Iq{Type: "set", Id: <- Id, Nested: &bindReq}
+ msg := &Iq{Type: "set", Id: <- Id, Nested: []interface{}{bindReq}}
f := func(st Stanza) bool {
if st.GetType() == "error" {
log.Println("Resource binding failed")
return false
}
- bindRepl, ok := st.GetNested().(*bindIq)
- if !ok {
- log.Printf("bad bind reply: %v", bindRepl)
+ var bindRepl *bindIq
+ for _, ele := range(st.GetNested()) {
+ if b, ok := ele.(*bindIq) ; ok {
+ bindRepl = b
+ break
+ }
+ }
+ if bindRepl == nil {
+ log.Printf("bad bind reply: %v", st)
return false
}
jidStr := bindRepl.Jid
--- a/structs.go Sat Jan 07 21:20:23 2012 -0700
+++ b/structs.go Sat Jan 07 22:22:18 2012 -0700
@@ -93,11 +93,10 @@
GetLang() string
// A nested error element, if any.
GetError() *Error
- // A (non-error) nested element, if any.
- // BUG(cjyar) This should return a slice.
- GetNested() interface{}
- setNested(interface{})
- generic() *Generic
+ // Zero or more (non-error) nested elements. These will be in
+ // namespaces managed by extensions.
+ GetNested() []interface{}
+ addNested(interface{})
innerxml() string
}
@@ -113,8 +112,7 @@
Subject *Generic
Body *Generic
Thread *Generic
- Any *Generic
- Nested interface{}
+ Nested []interface{}
}
var _ xml.Marshaler = &Message{}
var _ Stanza = &Message{}
@@ -131,8 +129,7 @@
Show *Generic
Status *Generic
Priority *Generic
- Any *Generic
- Nested interface{}
+ Nested []interface{}
}
var _ xml.Marshaler = &Presence{}
var _ Stanza = &Presence{}
@@ -146,8 +143,7 @@
Lang string `xml:"attr"`
Innerxml string `xml:"innerxml"`
Error *Error
- Any *Generic
- Nested interface{}
+ Nested []interface{}
}
var _ xml.Marshaler = &Iq{}
var _ Stanza = &Iq{}
@@ -335,10 +331,10 @@
return nil, err
}
}
- if st.GetNested() != nil {
- xml.Marshal(buf, st.GetNested())
- } else if st.generic() != nil {
- xml.Marshal(buf, st.generic())
+ if nested := st.GetNested() ; nested != nil {
+ for _, n := range(nested) {
+ xml.Marshal(buf, n)
+ }
} else if st.innerxml() != "" {
buf.WriteString(st.innerxml())
}
@@ -383,16 +379,12 @@
return m.Error
}
-func (m *Message) GetNested() interface{} {
+func (m *Message) GetNested() []interface{} {
return m.Nested
}
-func (m *Message) setNested(n interface{}) {
- m.Nested = n
-}
-
-func (m *Message) generic() *Generic {
- return m.Any
+func (m *Message) addNested(n interface{}) {
+ m.Nested = append(m.Nested, n)
}
func (m *Message) innerxml() string {
@@ -431,16 +423,12 @@
return p.Error
}
-func (p *Presence) GetNested() interface{} {
+func (p *Presence) GetNested() []interface{} {
return p.Nested
}
-func (p *Presence) setNested(n interface{}) {
- p.Nested = n
-}
-
-func (p *Presence) generic() *Generic {
- return p.Any
+func (p *Presence) addNested(n interface{}) {
+ p.Nested = append(p.Nested, n)
}
func (p *Presence) innerxml() string {
@@ -479,16 +467,12 @@
return iq.Error
}
-func (iq *Iq) GetNested() interface{} {
+func (iq *Iq) GetNested() []interface{} {
return iq.Nested
}
-func (iq *Iq) setNested(n interface{}) {
- iq.Nested = n
-}
-
-func (iq *Iq) generic() *Generic {
- return iq.Any
+func (iq *Iq) addNested(n interface{}) {
+ iq.Nested = append(iq.Nested, n)
}
func (iq *Iq) innerxml() string {
--- a/structs_test.go Sat Jan 07 21:20:23 2012 -0700
+++ b/structs_test.go Sat Jan 07 22:22:18 2012 -0700
@@ -83,8 +83,9 @@
}
func TestIqMarshal(t *testing.T) {
- iq := &Iq{Type: "set", Id: "3", Any: &Generic{XMLName:
- xml.Name{Space: NsBind, Local: "bind"}}}
+ iq := &Iq{Type: "set", Id: "3", Nested:
+ []interface{}{Generic{XMLName: xml.Name{Space: NsBind,
+ Local: "bind"}}}}
exp := `<iq id="3" type="set"><bind xmlns="` + NsBind +
`"></bind></iq>`
assertMarshal(t, exp, iq)
@@ -106,11 +107,10 @@
if st.GetError() != nil {
t.Errorf("iq: error %v", st.GetError())
}
- if st.generic() == nil {
- t.Errorf("iq: nil child")
+ if st.innerxml() == "" {
+ t.Errorf("iq: empty child")
}
- assertEquals(t, "foo", st.generic().XMLName.Local)
- assertEquals(t, "text", st.generic().Chardata)
+ assertEquals(t, "<foo>text</foo>", st.innerxml())
str = `<message to="alice" from="bob"/>`
st, err = ParseStanza(str)
@@ -125,8 +125,8 @@
if st.GetError() != nil {
t.Errorf("message: error %v", st.GetError())
}
- if st.generic() != nil {
- t.Errorf("message: child %v", st.generic())
+ if st.innerxml() != "" {
+ t.Errorf("message: child %v", st.innerxml())
}
str = `<presence/>`
--- a/xmpp.go Sat Jan 07 21:20:23 2012 -0700
+++ b/xmpp.go Sat Jan 07 22:22:18 2012 -0700
@@ -301,9 +301,9 @@
// Presence struct. See RFC 3921, Section 3.
func (cl *Client) StartSession(getRoster bool, pr *Presence) os.Error {
id := <- Id
- iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Any:
- &Generic{XMLName: xml.Name{Space: NsSession, Local:
- "session"}}}
+ iq := &Iq{To: cl.Jid.Domain, Id: id, Type: "set", Nested:
+ []interface{}{ Generic{XMLName: xml.Name{Space:
+ NsSession, Local: "session"}}}}
ch := make(chan os.Error)
f := func(st Stanza) bool {
if st.GetType() == "error" {