# HG changeset patch # User Chris Jones # Date 1326000138 25200 # Node ID 16513974d273f7d831a9cfd9ded36cef80df13fa # Parent 6d4f43f7dc1929b5b9a55423abbd630e29a09fdc Stanzas can now contain multiple nested (extended) elements. diff -r 6d4f43f7dc19 -r 16513974d273 roster.go --- 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 } diff -r 6d4f43f7dc19 -r 16513974d273 roster_test.go --- 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 := `` 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)) diff -r 6d4f43f7dc19 -r 16513974d273 stream.go --- 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 diff -r 6d4f43f7dc19 -r 16513974d273 structs.go --- 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 { diff -r 6d4f43f7dc19 -r 16513974d273 structs_test.go --- 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 := `` 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, "text", st.innerxml()) str = `` 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 = `` diff -r 6d4f43f7dc19 -r 16513974d273 xmpp.go --- 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" {