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={}):
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 isinstance(data, list) and len(data) > 0:
80 reg = self.getRegistry()
81 if reg.wireProtocol == 'array':
83 for i in range(len(reg.keys)):
85 self.__data[reg.keys[i]] = data[i]
87 self.__data[reg.keys[i]] = None
90 def osrfNetworkRegisterHint(hint, keys, type='hash'):
91 ''' Registers a new network-serializable object class.
93 'hint' is the class hint
94 'keys' is the list of field names on the object
95 If this is an array-based object, the field names
96 must be sorted to reflect the encoding order of the fields
97 'type' is the wire-protocol of the object. hash or array.
100 # register the class with the global registry
101 registry = osrfNetworkRegistry(hint, keys, type)
103 # create the new class locally with the given hint name
104 exec('class %s(osrfNetworkObject):\n\tpass' % hint)
106 # give the new registered class a local handle
108 exec('cls = %s' % hint)
110 # assign an accessor/mutator for each field on the object
112 __makeNetworkAccessor(cls, k)
114 # assign our custom init function
115 setattr(cls, '__init__', __osrfNetworkObjectInit)
116 __makeGetRegistry(cls, registry)
121 # attach our new class to the osrfNetworkObject
122 # class so others can access it
123 setattr(osrfNetworkObject, hint , cls)
128 # create a unknown object to handle unregistred types
129 osrfNetworkRegisterHint('__unknown', [], 'hash')
131 # -------------------------------------------------------------------
132 # Define the custom object parsing behavior
133 # -------------------------------------------------------------------
134 def parseNetObject(obj):
138 hint = obj[OSRF_JSON_CLASS_KEY]
139 subObj = obj[OSRF_JSON_PAYLOAD_KEY]
140 reg = osrfNetworkRegistry.getRegistry(hint)
144 if reg.wireProtocol == 'array':
145 for i in range(len(reg.keys)):
147 obj[reg.keys[i]] = parseNetObject(subObj[i])
149 obj[reg.keys[i]] = None
152 obj[k] = parseNetObject(subObj.get(k))
154 estr = 'obj = osrfNetworkObject.%s(obj)' % hint
158 # this object has not been registered, shove it into the default container
159 obj = osrfNetworkObject.__unknown(obj)
165 # the current object does not have a class hint
166 if isinstance(obj, list):
167 for i in range(len(obj)):
168 obj[i] = parseNetObject(obj[i])
171 if isinstance(obj, dict):
172 for k,v in obj.iteritems():
173 obj[k] = parseNetObject(v)
178 def osrfObjectToXML(obj):
179 """ Returns the XML representation of an internal object."""
181 __osrfObjectToXML(obj, chars)
182 return ''.join(chars)
184 def __osrfObjectToXML(obj, chars):
185 """ Turns an internal object into OpenSRF XML """
188 chars.append('<null/>')
191 if isinstance(obj, unicode) or isinstance(obj, str):
192 chars.append('<string>%s</string>' % saxutils.escape(obj))
195 if isinstance(obj, int) or isinstance(obj, long):
196 chars.append('<number>%d</number>' % obj)
199 if isinstance(obj, float):
200 chars.append('<number>%f</number>' % obj)
205 if isinstance(obj, osrfNetworkObject):
207 registry = obj.getRegistry()
209 hint = saxutils.escape(registry.hint)
211 if registry.wireProtocol == 'array':
212 chars.append("<array class_hint='%s'>" % hint)
213 for k in registry.keys:
214 __osrfObjectToXML(data.get(k), chars)
215 chars.append('</array>')
218 if registry.wireProtocol == 'hash':
219 chars.append("<object class_hint='%s'>" % hint)
220 for k,v in data.items():
221 chars.append("<element key='%s'>" % saxutils.escape(k))
222 __osrfObjectToXML(v, chars)
223 chars.append('</element>')
224 chars.append('</object>')
227 if isinstance(obj, list):
228 chars.append('<array>')
230 __osrfObjectToXML(i, chars)
231 chars.append('</array>')
234 if isinstance(obj, dict):
235 chars.append('<object>')
236 for k,v in obj.items():
237 chars.append("<element key='%s'>" % saxutils.escape(k))
238 __osrfObjectToXML(v, chars)
239 chars.append('</element>')
240 chars.append('</object>')
243 if isinstance(obj, bool):
246 chars.append("<boolean value='%s'/>" % val)