~certify-web-dev/twisted/certify-trunk

« back to all changes in this revision

Viewing changes to twisted/words/xish/xmlstream.py

  • Committer: Bazaar Package Importer
  • Author(s): Matthias Klose
  • Date: 2007-01-17 14:52:35 UTC
  • mfrom: (1.1.5 upstream) (2.1.2 etch)
  • Revision ID: james.westby@ubuntu.com-20070117145235-btmig6qfmqfen0om
Tags: 2.5.0-0ubuntu1
New upstream version, compatible with python2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- test-case-name: twisted.words.test.test_xmlstream -*-
 
2
#
 
3
# Copyright (c) 2001-2005 Twisted Matrix Laboratories.
 
4
# See LICENSE for details.
 
5
 
 
6
""" XML Stream processing.
 
7
 
 
8
An XML Stream is defined as a connection over which two XML documents are
 
9
exchanged during the lifetime of the connection, one for each direction. The
 
10
unit of interaction is a direct child element of the root element (stanza).
 
11
 
 
12
The most prominent use of XML Streams is Jabber, but this module is generically
 
13
usable. See Twisted Words for Jabber specific protocol support.
 
14
 
 
15
Maintainer: U{Ralph Meijer<mailto:twisted@ralphm.ik.nu>}
 
16
"""
 
17
 
 
18
from twisted.internet import protocol
 
19
from twisted.words.xish import domish, utility
 
20
 
 
21
STREAM_CONNECTED_EVENT = intern("//event/stream/connected")
 
22
STREAM_START_EVENT = intern("//event/stream/start")
 
23
STREAM_END_EVENT = intern("//event/stream/end")
 
24
STREAM_ERROR_EVENT = intern("//event/stream/error")
 
25
 
 
26
class XmlStream(protocol.Protocol, utility.EventDispatcher):
 
27
    """ Generic Streaming XML protocol handler.
 
28
 
 
29
    This protocol handler will parse incoming data as XML and dispatch events
 
30
    accordingly. Incoming stanzas can be handled by registering observers using
 
31
    XPath-like expressions that are matched against each stanza. See
 
32
    L{utility.EventDispatcher} for details.
 
33
    """
 
34
    def __init__(self):
 
35
        utility.EventDispatcher.__init__(self)
 
36
        self.stream = None
 
37
        self.rawDataOutFn = None
 
38
        self.rawDataInFn = None
 
39
 
 
40
    def _initializeStream(self):
 
41
        """ Sets up XML Parser. """
 
42
        self.stream = domish.elementStream()
 
43
        self.stream.DocumentStartEvent = self.onDocumentStart
 
44
        self.stream.ElementEvent = self.onElement
 
45
        self.stream.DocumentEndEvent = self.onDocumentEnd
 
46
 
 
47
    ### --------------------------------------------------------------
 
48
    ###
 
49
    ### Protocol events
 
50
    ###
 
51
    ### --------------------------------------------------------------
 
52
 
 
53
    def connectionMade(self):
 
54
        """ Called when a connection is made.
 
55
 
 
56
        Sets up the XML parser and dispatches the L{STREAM_CONNECTED_EVENT}
 
57
        event indicating the connection has been established.
 
58
        """
 
59
        self._initializeStream()
 
60
        self.dispatch(self, STREAM_CONNECTED_EVENT)
 
61
 
 
62
    def dataReceived(self, data):
 
63
        """ Called whenever data is received.
 
64
 
 
65
        Passes the data to the XML parser. This can result in calls to the
 
66
        DOM handlers. If a parse error occurs, the L{STREAM_ERROR_EVENT} event
 
67
        is called to allow for cleanup actions, followed by dropping the
 
68
        connection.
 
69
        """
 
70
        try:
 
71
            if self.rawDataInFn: self.rawDataInFn(data)
 
72
            self.stream.parse(data)
 
73
        except domish.ParserError:
 
74
            self.dispatch(self, STREAM_ERROR_EVENT)
 
75
            self.transport.loseConnection()
 
76
 
 
77
    def connectionLost(self, reason):
 
78
        """ Called when the connection is shut down.
 
79
 
 
80
        Dispatches the L{STREAM_END_EVENT}.
 
81
        """
 
82
        self.dispatch(self, STREAM_END_EVENT)
 
83
        self.stream = None
 
84
        
 
85
    ### --------------------------------------------------------------
 
86
    ###
 
87
    ### DOM events
 
88
    ###
 
89
    ### --------------------------------------------------------------
 
90
 
 
91
    def onDocumentStart(self, rootelem):
 
92
        """ Called whenever the start tag of a root element has been received.
 
93
 
 
94
        Dispatches the L{STREAM_START_EVENT}.
 
95
        """
 
96
        self.dispatch(self, STREAM_START_EVENT)    
 
97
 
 
98
    def onElement(self, element):
 
99
        """ Called whenever a direct child element of the root element has
 
100
        been received.
 
101
 
 
102
        Dispatches the received element.
 
103
        """
 
104
        self.dispatch(element)
 
105
 
 
106
    def onDocumentEnd(self):
 
107
        """ Called whenever the end tag of the root element has been received.
 
108
 
 
109
        Closes the connection. This causes C{connectionLost} being called.
 
110
        """
 
111
        self.transport.loseConnection()
 
112
 
 
113
    def setDispatchFn(self, fn):
 
114
        """ Set another function to handle elements. """
 
115
        self.stream.ElementEvent = fn
 
116
 
 
117
    def resetDispatchFn(self):
 
118
        """ Set the default function (C{onElement}) to handle elements. """
 
119
        self.stream.ElementEvent = self.onElement
 
120
 
 
121
    def send(self, obj):
 
122
        """ Send data over the stream.
 
123
 
 
124
        Sends the given C{obj} over the connection. C{obj} may be instances of
 
125
        L{domish.Element}, L{unicode} and L{str}. The first two will be
 
126
        properly serialized and/or encoded. L{str} objects must be in UTF-8
 
127
        encoding.
 
128
 
 
129
        Note: because it is easy to make mistakes in maintaining a properly
 
130
        encoded L{str} object, it is advised to use L{unicode} objects
 
131
        everywhere when dealing with XML Streams.
 
132
 
 
133
        @param obj: Object to be sent over the stream.
 
134
        @type obj: L{domish.Element}, L{domish} or L{str}
 
135
 
 
136
        """
 
137
        if domish.IElement.providedBy(obj):
 
138
            obj = obj.toXml()
 
139
            
 
140
        if isinstance(obj, unicode):
 
141
            obj = obj.encode('utf-8')
 
142
            
 
143
        if self.rawDataOutFn:
 
144
            self.rawDataOutFn(obj)
 
145
            
 
146
        self.transport.write(obj)
 
147
 
 
148
 
 
149
class XmlStreamFactory(protocol.ReconnectingClientFactory):
 
150
    """ Factory for XmlStream protocol objects as a reconnection client.
 
151
    
 
152
    This factory generates XmlStream objects when a connection has been
 
153
    established. To make sure certain event observers are set up before
 
154
    incoming data is processed, you can set up bootstrap event observers using
 
155
    C{addBootstrap}.
 
156
    """
 
157
 
 
158
    def __init__(self):
 
159
        self.bootstraps = []
 
160
 
 
161
    def buildProtocol(self, addr):
 
162
        """ Create an instance of XmlStream.
 
163
 
 
164
        The returned instance will have bootstrap event observers registered
 
165
        and will proceed to handle input on an incoming connection.
 
166
        """
 
167
        self.resetDelay()
 
168
        xs = XmlStream()
 
169
        xs.factory = self
 
170
        for event, fn in self.bootstraps: xs.addObserver(event, fn)
 
171
        return xs
 
172
 
 
173
    def addBootstrap(self, event, fn):
 
174
        """ Add a bootstrap event handler. """
 
175
        self.bootstraps.append((event, fn))
 
176
 
 
177
    def removeBootstrap(self, event, fn):
 
178
        """ Remove a bootstrap event handler. """
 
179
        self.bootstraps.remove((event, fn))