]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/python/osrf/net_obj.py
added error message. re-tabbed to 4 spaces
[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 # -----------------------------------------------------------
6 # Define the global network-class registry
7 # -----------------------------------------------------------
8
9 # Global object registry 
10 objectRegistry = {}
11
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).  
16         '''
17
18     def __init__(self, hint, keys, wireProtocol):
19         global objectRegistry
20         self.hint = hint
21         self.keys = keys
22         self.wireProtocol = wireProtocol
23         objectRegistry[hint] = self
24     
25     def getRegistry(hint):
26         global objectRegistry
27         return objectRegistry.get(hint)
28     getRegistry = staticmethod(getRegistry)
29
30
31 # -----------------------------------------------------------
32 # Define the base class for all network-serializable objects
33 # -----------------------------------------------------------
34
35 class osrfNetworkObject(object):
36     ''' Base class for all network serializable objects '''
37
38     # link to our registry object for this registered class
39     registry = None
40
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 '''
44
45         self._data = data
46         if not data: self._data = {}
47         if isinstance(data, list):
48             self.importArrayData(list)
49
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 '''
53
54         self._data = {}
55         if len(data) == 0: return
56
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])
62
63     def getData(self):
64         ''' Returns the full dataset for this object as a dict '''
65         return self._data
66
67     def setField(self, field, value):
68         self._data[field] = value
69
70     def getField(self, field):
71         return self._data.get(field)
72
73     def getRegistry(cls):
74         ''' Returns the registry object for this registered class '''
75         return cls.registry
76     getRegistry = classmethod(getRegistry)
77
78
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'''
83     try:
84         obj = None
85         exec('obj = osrfNetworkObject.%s()' % hint)
86         return obj
87     except AttributeError:
88         return osrfNetworkObject.__unknown()
89
90
91
92
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):
98         if len(args) != 0:
99             self.setField(key, args[0])
100         return self.getField(key)
101     setattr(cls, key, accessor)
102
103
104 def osrfNetworkRegisterHint(hint, keys, type='hash'):
105     ''' Registers a new network-serializable object class.
106
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.
112         '''
113
114     # register the class with the global registry
115     registry = osrfNetworkRegistry(hint, keys, type)
116
117     # create the new class locally with the given hint name
118     exec('class %s(osrfNetworkObject):\n\tpass' % hint)
119
120     # give the new registered class a local handle
121     cls = None
122     exec('cls = %s' % hint)
123
124     # assign an accessor/mutator for each field on the object
125     for k in keys:
126         __makeNetworkAccessor(cls, k)
127
128     # attach our new class to the osrfNetworkObject 
129     # class so others can access it
130     setattr(osrfNetworkObject, hint , cls)
131     cls.registry = registry
132
133
134
135
136 # create a unknown object to handle unregistred types
137 osrfNetworkRegisterHint('__unknown', [], 'hash')
138
139 # -------------------------------------------------------------------
140 # Define the custom object parsing behavior 
141 # -------------------------------------------------------------------
142 def parseNetObject(obj):
143     
144     try:
145
146         hint = obj[OSRF_JSON_CLASS_KEY]
147         subObj = obj[OSRF_JSON_PAYLOAD_KEY]
148         reg = osrfNetworkRegistry.getRegistry(hint)
149
150         obj = {}
151
152         if reg.wireProtocol == 'array':
153             for i in range(len(reg.keys)):
154                 if len(subObj) > i:
155                     obj[reg.keys[i]] = parseNetObject(subObj[i])
156                 else:
157                     obj[reg.keys[i]] = None
158         else:
159             for k in reg.keys:
160                 obj[k] = parseNetObject(subObj.get(k))
161
162         estr = 'obj = osrfNetworkObject.%s(obj)' % hint
163         try:
164             exec(estr)
165         except e:
166             # this object has not been registered, shove it into the default container
167             obj = osrfNetworkObject.__unknown(obj)
168
169         return obj
170
171     except: pass
172
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])
177
178     else:
179         if isinstance(obj, dict):
180             for k,v in obj.iteritems():
181                 obj[k] = parseNetObject(v)
182
183     return obj;
184
185
186 def osrfObjectToXML(obj):
187     """ Returns the XML representation of an internal object."""
188     chars = []
189     __osrfObjectToXML(obj, chars)
190     return ''.join(chars)
191
192 def __osrfObjectToXML(obj, chars):
193     """ Turns an internal object into OpenSRF XML """
194
195     if obj is None:
196         chars.append('<null/>')
197         return
198
199     if isinstance(obj, unicode) or isinstance(obj, str):
200         chars.append('<string>%s</string>' % saxutils.escape(obj))
201         return
202
203     if isinstance(obj, int)  or isinstance(obj, long):
204         chars.append('<number>%d</number>' % obj)
205         return
206
207     if isinstance(obj, float):
208         chars.append('<number>%f</number>' % obj)
209         return
210
211     classHint = None
212
213     if isinstance(obj, osrfNetworkObject): 
214
215         registry = obj.getRegistry()
216         data = obj.getData()
217         hint = saxutils.escape(registry.hint)
218
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>')
224
225         else:
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>')
233                 
234
235     if isinstance(obj, list):
236         chars.append('<array>')
237         for i in obj:
238             __osrfObjectToXML(i, chars)
239         chars.append('</array>')
240         return
241
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>')
249         return
250
251     if isinstance(obj, bool):
252         val = 'false'
253         if obj: val = 'true'
254         chars.append("<boolean value='%s'/>" % val)
255         return
256