]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/python/osrf/net_obj.py
8ae3b9b42bfc8e356c029d9f029ce56ff90183c1
[OpenSRF.git] / src / python / osrf / net_obj.py
1 from osrf.const import OSRF_JSON_PAYLOAD_KEY, OSRF_JSON_CLASS_KEY
2 from xml.sax import saxutils
3
4
5 class osrfNetworkObject(object):
6     ''' Base class for all network serializable objects '''
7     pass
8
9 def osrfNewObjectFromHint(hint):
10     try:
11         obj = None
12         exec('obj = osrfNetworkObject.%s()' % hint)
13         return obj
14     except AttributeError:
15         return osrfNetworkObject.__unknown()
16
17
18 ''' Global object registry '''
19 objectRegistry = {}
20
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).  
25         '''
26
27     def __init__(self, hint, keys, wireProtocol):
28         global objectRegistry
29         self.hint = hint
30         self.keys = keys
31         self.wireProtocol = wireProtocol
32         objectRegistry[hint] = self
33     
34     def getRegistry(hint):
35         global objectRegistry
36         return objectRegistry.get(hint)
37     getRegistry = staticmethod(getRegistry)
38
39
40 def __makeNetworkAccessor(cls, key):
41     '''  Creates and accessor/mutator method for the given class.  
42
43         'key' is the name the method will have and represents
44         the field on the object whose data we are accessing
45         ''' 
46     def accessor(self, *args):
47         if len(args) != 0:
48             self.__data[key] = args[0]
49         return self.__data.get(key)
50     setattr(cls, key, accessor)
51
52
53
54 def __makeGetRegistry(cls, registry):
55     ''' Wraps the registry for this class inside an accessor method '''
56     def get(self):
57         return registry
58     setattr(cls, 'getRegistry', get)
59
60 def __makeGetData(cls):
61     ''' Wraps the stored data in an accessor method '''
62     def get(self):
63         return self.__data
64     setattr(cls, 'getData', get)
65
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)
71         
72
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
77         '''
78     self.__data = data
79     if isinstance(data, list) and len(data) > 0:
80         reg = self.getRegistry()
81         if reg.wireProtocol == 'array':
82             self.__data = {}
83             for i in range(len(reg.keys)):
84                 try:
85                     self.__data[reg.keys[i]] = data[i]
86                 except:
87                     self.__data[reg.keys[i]] = None
88
89
90 def osrfNetworkRegisterHint(hint, keys, type='hash'):
91     ''' Registers a new network-serializable object class.
92
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.
98         '''
99
100     # register the class with the global registry
101     registry = osrfNetworkRegistry(hint, keys, type)
102
103     # create the new class locally with the given hint name
104     exec('class %s(osrfNetworkObject):\n\tpass' % hint)
105
106     # give the new registered class a local handle
107     cls = None
108     exec('cls = %s' % hint)
109
110     # assign an accessor/mutator for each field on the object
111     for k in keys:
112         __makeNetworkAccessor(cls, k)
113
114     # assign our custom init function
115     setattr(cls, '__init__', __osrfNetworkObjectInit)
116     __makeGetRegistry(cls, registry)
117     __makeGetData(cls)
118     __makeSetField(cls)
119
120
121     # attach our new class to the osrfNetworkObject 
122     # class so others can access it
123     setattr(osrfNetworkObject, hint , cls)
124
125
126
127
128 # create a unknown object to handle unregistred types
129 osrfNetworkRegisterHint('__unknown', [], 'hash')
130
131 # -------------------------------------------------------------------
132 # Define the custom object parsing behavior 
133 # -------------------------------------------------------------------
134 def parseNetObject(obj):
135     
136     try:
137
138         hint = obj[OSRF_JSON_CLASS_KEY]
139         subObj = obj[OSRF_JSON_PAYLOAD_KEY]
140         reg = osrfNetworkRegistry.getRegistry(hint)
141
142         obj = {}
143
144         if reg.wireProtocol == 'array':
145             for i in range(len(reg.keys)):
146                 if len(subObj) > i:
147                     obj[reg.keys[i]] = parseNetObject(subObj[i])
148                 else:
149                     obj[reg.keys[i]] = None
150         else:
151             for k in reg.keys:
152                 obj[k] = parseNetObject(subObj.get(k))
153
154         estr = 'obj = osrfNetworkObject.%s(obj)' % hint
155         try:
156             exec(estr)
157         except e:
158             # this object has not been registered, shove it into the default container
159             obj = osrfNetworkObject.__unknown(obj)
160
161         return obj
162
163     except: pass
164
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])
169
170     else:
171         if isinstance(obj, dict):
172             for k,v in obj.iteritems():
173                 obj[k] = parseNetObject(v)
174
175     return obj;
176
177
178 def osrfObjectToXML(obj):
179     """ Returns the XML representation of an internal object."""
180     chars = []
181     __osrfObjectToXML(obj, chars)
182     return ''.join(chars)
183
184 def __osrfObjectToXML(obj, chars):
185     """ Turns an internal object into OpenSRF XML """
186
187     if obj is None:
188         chars.append('<null/>')
189         return
190
191     if isinstance(obj, unicode) or isinstance(obj, str):
192         chars.append('<string>%s</string>' % saxutils.escape(obj))
193         return
194
195     if isinstance(obj, int)  or isinstance(obj, long):
196         chars.append('<number>%d</number>' % obj)
197         return
198
199     if isinstance(obj, float):
200         chars.append('<number>%f</number>' % obj)
201         return
202
203     classHint = None
204
205     if isinstance(obj, osrfNetworkObject): 
206
207         registry = obj.getRegistry()
208         data = obj.getData()
209         hint = saxutils.escape(registry.hint)
210
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>')
216
217         else:
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>')
225                 
226
227     if isinstance(obj, list):
228         chars.append('<array>')
229         for i in obj:
230             __osrfObjectToXML(i, chars)
231         chars.append('</array>')
232         return
233
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>')
241         return
242
243     if isinstance(obj, bool):
244         val = 'false'
245         if obj: val = 'true'
246         chars.append("<boolean value='%s'/>" % val)
247         return
248