1 from osrf.const import OSRF_JSON_PAYLOAD_KEY, OSRF_JSON_CLASS_KEY
2 from xml.sax import saxutils
5 # -----------------------------------------------------------
6 # Define the global network-class registry
7 # -----------------------------------------------------------
9 # Global object registry
12 class osrfNetworkRegistry(object):
13 ''' Network-serializable objects must be registered. The class
14 hint maps to a set (ordered in the case of array-base objects)
15 of field names (keys).
18 def __init__(self, hint, keys, wireProtocol):
22 self.wireProtocol = wireProtocol
23 objectRegistry[hint] = self
25 def getRegistry(hint):
27 return objectRegistry.get(hint)
28 getRegistry = staticmethod(getRegistry)
31 # -----------------------------------------------------------
32 # Define the base class for all network-serializable objects
33 # -----------------------------------------------------------
35 class osrfNetworkObject(object):
36 ''' Base class for all network serializable objects '''
38 # link to our registry object for this registered class
41 def __init__(self, data=None):
42 ''' If this is an array, we pull data out of the data array
43 (if there is any) and translate that into a hash internally '''
46 if not data: self._data = {}
47 if isinstance(data, list):
48 self.importArrayData(list)
50 def importArrayData(self, data):
51 ''' If an array-based object is created with an array
52 of data, cycle through and load the data '''
55 if len(data) == 0: return
57 reg = self.getRegistry()
58 if reg.wireProtocol == 'array':
59 for i in range(len(reg.keys)):
60 if len(data) > i: break
61 self.setField(reg.keys[i], data[i])
64 ''' Returns the full dataset for this object as a dict '''
67 def setField(self, field, value):
68 self._data[field] = value
70 def getField(self, field):
71 return self._data.get(field)
74 ''' Returns the registry object for this registered class '''
76 getRegistry = classmethod(getRegistry)
79 def osrfNewObjectFromHint(hint):
80 ''' Given a hint, this will create a new object of that
81 type and return it. If this hint is not registered,
82 an object of type osrfNetworkObject.__unknown is returned'''
85 exec('obj = osrfNetworkObject.%s()' % hint)
87 except AttributeError:
88 return osrfNetworkObject.__unknown()
93 def __makeNetworkAccessor(cls, key):
94 ''' Creates and accessor/mutator method for the given class.
95 'key' is the name the method will have and represents
96 the field on the object whose data we are accessing '''
97 def accessor(self, *args):
99 self.setField(key, args[0])
100 return self.getField(key)
101 setattr(cls, key, accessor)
104 def osrfNetworkRegisterHint(hint, keys, type='hash'):
105 ''' Registers a new network-serializable object class.
107 'hint' is the class hint
108 'keys' is the list of field names on the object
109 If this is an array-based object, the field names
110 must be sorted to reflect the encoding order of the fields
111 'type' is the wire-protocol of the object. hash or array.
114 # register the class with the global registry
115 registry = osrfNetworkRegistry(hint, keys, type)
117 # create the new class locally with the given hint name
118 exec('class %s(osrfNetworkObject):\n\tpass' % hint)
120 # give the new registered class a local handle
122 exec('cls = %s' % hint)
124 # assign an accessor/mutator for each field on the object
126 __makeNetworkAccessor(cls, k)
128 # attach our new class to the osrfNetworkObject
129 # class so others can access it
130 setattr(osrfNetworkObject, hint , cls)
131 cls.registry = registry
136 # create a unknown object to handle unregistred types
137 osrfNetworkRegisterHint('__unknown', [], 'hash')
139 # -------------------------------------------------------------------
140 # Define the custom object parsing behavior
141 # -------------------------------------------------------------------
142 def parseNetObject(obj):
146 hint = obj[OSRF_JSON_CLASS_KEY]
147 subObj = obj[OSRF_JSON_PAYLOAD_KEY]
148 reg = osrfNetworkRegistry.getRegistry(hint)
152 if reg.wireProtocol == 'array':
153 for i in range(len(reg.keys)):
155 obj[reg.keys[i]] = parseNetObject(subObj[i])
157 obj[reg.keys[i]] = None
160 obj[k] = parseNetObject(subObj.get(k))
162 estr = 'obj = osrfNetworkObject.%s(obj)' % hint
166 # this object has not been registered, shove it into the default container
167 obj = osrfNetworkObject.__unknown(obj)
173 # the current object does not have a class hint
174 if isinstance(obj, list):
175 for i in range(len(obj)):
176 obj[i] = parseNetObject(obj[i])
179 if isinstance(obj, dict):
180 for k,v in obj.iteritems():
181 obj[k] = parseNetObject(v)
186 def osrfObjectToXML(obj):
187 """ Returns the XML representation of an internal object."""
189 __osrfObjectToXML(obj, chars)
190 return ''.join(chars)
192 def __osrfObjectToXML(obj, chars):
193 """ Turns an internal object into OpenSRF XML """
196 chars.append('<null/>')
199 if isinstance(obj, unicode) or isinstance(obj, str):
200 chars.append('<string>%s</string>' % saxutils.escape(obj))
203 if isinstance(obj, int) or isinstance(obj, long):
204 chars.append('<number>%d</number>' % obj)
207 if isinstance(obj, float):
208 chars.append('<number>%f</number>' % obj)
213 if isinstance(obj, osrfNetworkObject):
215 registry = obj.getRegistry()
217 hint = saxutils.escape(registry.hint)
219 if registry.wireProtocol == 'array':
220 chars.append("<array class_hint='%s'>" % hint)
221 for k in registry.keys:
222 __osrfObjectToXML(data.get(k), chars)
223 chars.append('</array>')
226 if registry.wireProtocol == 'hash':
227 chars.append("<object class_hint='%s'>" % hint)
228 for k,v in data.items():
229 chars.append("<element key='%s'>" % saxutils.escape(k))
230 __osrfObjectToXML(v, chars)
231 chars.append('</element>')
232 chars.append('</object>')
235 if isinstance(obj, list):
236 chars.append('<array>')
238 __osrfObjectToXML(i, chars)
239 chars.append('</array>')
242 if isinstance(obj, dict):
243 chars.append('<object>')
244 for k,v in obj.items():
245 chars.append("<element key='%s'>" % saxutils.escape(k))
246 __osrfObjectToXML(v, chars)
247 chars.append('</element>')
248 chars.append('</object>')
251 if isinstance(obj, bool):
254 chars.append("<boolean value='%s'/>" % val)