1 from osrf.const import OSRF_JSON_PAYLOAD_KEY, OSRF_JSON_CLASS_KEY
2 from xml.sax import saxutils
5 class osrfNetworkObject(object):
6 ''' Base class for all network serializable objects '''
9 def osrfNewObjectFromHint(hint):
12 exec('obj = osrfNetworkObject.%s()' % hint)
14 except AttributeError:
15 return osrfNetworkObject.__unknown()
18 ''' Global object registry '''
21 class osrfNetworkRegistry(object):
22 ''' Network-serializable objects must be registered. The class
23 hint maps to a set (ordered in the case of array-base objects)
24 of field names (keys).
27 def __init__(self, hint, keys, wireProtocol):
31 self.wireProtocol = wireProtocol
32 objectRegistry[hint] = self
34 def getRegistry(hint):
36 return objectRegistry.get(hint)
37 getRegistry = staticmethod(getRegistry)
40 def __makeNetworkAccessor(cls, key):
41 ''' Creates and accessor/mutator method for the given class.
43 'key' is the name the method will have and represents
44 the field on the object whose data we are accessing
46 def accessor(self, *args):
48 self.__data[key] = args[0]
49 return self.__data.get(key)
50 setattr(cls, key, accessor)
54 def __makeGetRegistry(cls, registry):
55 ''' Wraps the registry for this class inside an accessor method '''
58 setattr(cls, 'getRegistry', get)
60 def __makeGetData(cls):
61 ''' Wraps the stored data in an accessor method '''
64 setattr(cls, 'getData', get)
66 def __makeSetField(cls):
67 ''' Creates a generic mutator for fields by fieldname '''
68 def set(self, field, value):
69 self.__data[field] = value
70 setattr(cls, 'setField', set)
73 def __osrfNetworkObjectInit(self, data=None):
74 ''' __init__ method for osrNetworkObjects.
75 If this is an array, we pull data out of the data array
76 (if there is any) and translate that into a hash internally
79 if not data: self.__data = {}
81 if isinstance(data, list):
84 reg = self.getRegistry()
85 if reg.wireProtocol == 'array':
86 for i in range(len(reg.keys)):
88 self.__data[reg.keys[i]] = data[i]
90 self.__data[reg.keys[i]] = None
93 def osrfNetworkRegisterHint(hint, keys, type='hash'):
94 ''' Registers a new network-serializable object class.
96 'hint' is the class hint
97 'keys' is the list of field names on the object
98 If this is an array-based object, the field names
99 must be sorted to reflect the encoding order of the fields
100 'type' is the wire-protocol of the object. hash or array.
103 # register the class with the global registry
104 registry = osrfNetworkRegistry(hint, keys, type)
106 # create the new class locally with the given hint name
107 exec('class %s(osrfNetworkObject):\n\tpass' % hint)
109 # give the new registered class a local handle
111 exec('cls = %s' % hint)
113 # assign an accessor/mutator for each field on the object
115 __makeNetworkAccessor(cls, k)
117 # assign our custom init function
118 setattr(cls, '__init__', __osrfNetworkObjectInit)
119 __makeGetRegistry(cls, registry)
124 # attach our new class to the osrfNetworkObject
125 # class so others can access it
126 setattr(osrfNetworkObject, hint , cls)
131 # create a unknown object to handle unregistred types
132 osrfNetworkRegisterHint('__unknown', [], 'hash')
134 # -------------------------------------------------------------------
135 # Define the custom object parsing behavior
136 # -------------------------------------------------------------------
137 def parseNetObject(obj):
141 hint = obj[OSRF_JSON_CLASS_KEY]
142 subObj = obj[OSRF_JSON_PAYLOAD_KEY]
143 reg = osrfNetworkRegistry.getRegistry(hint)
147 if reg.wireProtocol == 'array':
148 for i in range(len(reg.keys)):
150 obj[reg.keys[i]] = parseNetObject(subObj[i])
152 obj[reg.keys[i]] = None
155 obj[k] = parseNetObject(subObj.get(k))
157 estr = 'obj = osrfNetworkObject.%s(obj)' % hint
161 # this object has not been registered, shove it into the default container
162 obj = osrfNetworkObject.__unknown(obj)
168 # the current object does not have a class hint
169 if isinstance(obj, list):
170 for i in range(len(obj)):
171 obj[i] = parseNetObject(obj[i])
174 if isinstance(obj, dict):
175 for k,v in obj.iteritems():
176 obj[k] = parseNetObject(v)
181 def osrfObjectToXML(obj):
182 """ Returns the XML representation of an internal object."""
184 __osrfObjectToXML(obj, chars)
185 return ''.join(chars)
187 def __osrfObjectToXML(obj, chars):
188 """ Turns an internal object into OpenSRF XML """
191 chars.append('<null/>')
194 if isinstance(obj, unicode) or isinstance(obj, str):
195 chars.append('<string>%s</string>' % saxutils.escape(obj))
198 if isinstance(obj, int) or isinstance(obj, long):
199 chars.append('<number>%d</number>' % obj)
202 if isinstance(obj, float):
203 chars.append('<number>%f</number>' % obj)
208 if isinstance(obj, osrfNetworkObject):
210 registry = obj.getRegistry()
212 hint = saxutils.escape(registry.hint)
214 if registry.wireProtocol == 'array':
215 chars.append("<array class_hint='%s'>" % hint)
216 for k in registry.keys:
217 __osrfObjectToXML(data.get(k), chars)
218 chars.append('</array>')
221 if registry.wireProtocol == 'hash':
222 chars.append("<object class_hint='%s'>" % hint)
223 for k,v in data.items():
224 chars.append("<element key='%s'>" % saxutils.escape(k))
225 __osrfObjectToXML(v, chars)
226 chars.append('</element>')
227 chars.append('</object>')
230 if isinstance(obj, list):
231 chars.append('<array>')
233 __osrfObjectToXML(i, chars)
234 chars.append('</array>')
237 if isinstance(obj, dict):
238 chars.append('<object>')
239 for k,v in obj.items():
240 chars.append("<element key='%s'>" % saxutils.escape(k))
241 __osrfObjectToXML(v, chars)
242 chars.append('</element>')
243 chars.append('</object>')
246 if isinstance(obj, bool):
249 chars.append("<boolean value='%s'/>" % val)