]> git.evergreen-ils.org Git - OpenSRF.git/blob - src/python/osrf/net_obj.py
792c613aa470229a943ff0dc446c6df674ef8e8c
[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 #newFromHint = staticmethod(newFromHint)
17
18
19 ''' Global object registry '''
20 objectRegistry = {}
21
22 class osrfNetworkRegistry(object):
23     ''' Network-serializable objects must be registered.  The class
24         hint maps to a set (ordered in the case of array-base objects)
25         of field names (keys).  
26         '''
27
28     def __init__(self, hint, keys, wireProtocol):
29         global objectRegistry
30         self.hint = hint
31         self.keys = keys
32         self.wireProtocol = wireProtocol
33         objectRegistry[hint] = self
34     
35     def getRegistry(hint):
36         global objectRegistry
37         return objectRegistry.get(hint)
38     getRegistry = staticmethod(getRegistry)
39
40
41 def __makeNetworkAccessor(cls, key):
42     '''  Creates and accessor/mutator method for the given class.  
43
44         'key' is the name the method will have and represents
45         the field on the object whose data we are accessing
46         ''' 
47     def accessor(self, *args):
48         if len(args) != 0:
49             self.__data[key] = args[0]
50         return self.__data.get(key)
51     setattr(cls, key, accessor)
52
53
54
55 def __makeGetRegistry(cls, registry):
56     ''' Wraps the registry for this class inside an accessor method '''
57     def get(self):
58         return registry
59     setattr(cls, 'getRegistry', get)
60
61 def __makeGetData(cls):
62     ''' Wraps the stored data in an accessor method '''
63     def get(self):
64         return self.__data
65     setattr(cls, 'getData', get)
66
67 def __makeSetField(cls):
68     ''' Creates a generic mutator for fields by fieldname '''
69     def set(self, field, value):
70         self.__data[field] = value
71     setattr(cls, 'setField', set)
72         
73
74 def __osrfNetworkObjectInit(self, data={}):
75     ''' __init__ method for osrNetworkObjects.
76         If this is an array, we pull data out of the data array
77         (if there is any) and translate that into a hash internally
78         '''
79
80     self.__data = data
81     if len(data) > 0:
82         reg = self.getRegistry()
83         if reg.wireProtocol == 'array':
84             self.__data = {}
85             for i in range(len(reg.keys)):
86                 try:
87                     self.__data[reg.keys[i]] = data[i]
88                 except:
89                     self.__data[reg.keys[i]] = None
90
91
92 def osrfNetworkRegisterHint(hint, keys, type='hash'):
93     ''' Registers a new network-serializable object class.
94
95         'hint' is the class hint
96         'keys' is the list of field names on the object
97             If this is an array-based object, the field names
98             must be sorted to reflect the encoding order of the fields
99         'type' is the wire-protocol of the object.  hash or array.
100         '''
101
102     # register the class with the global registry
103     registry = osrfNetworkRegistry(hint, keys, type)
104
105     # create the new class locally with the given hint name
106     exec('class %s(osrfNetworkObject):\n\tpass' % hint)
107
108     # give the new registered class a local handle
109     cls = None
110     exec('cls = %s' % hint)
111
112     # assign an accessor/mutator for each field on the object
113     for k in keys:
114         __makeNetworkAccessor(cls, k)
115
116     # assign our custom init function
117     setattr(cls, '__init__', __osrfNetworkObjectInit)
118     __makeGetRegistry(cls, registry)
119     __makeGetData(cls)
120     __makeSetField(cls)
121
122
123     # attach our new class to the osrfNetworkObject 
124     # class so others can access it
125     setattr(osrfNetworkObject, hint , cls)
126
127
128
129
130 # create a unknown object to handle unregistred types
131 osrfNetworkRegisterHint('__unknown', [], 'hash')
132
133 # -------------------------------------------------------------------
134 # Define the custom object parsing behavior 
135 # -------------------------------------------------------------------
136 def parseNetObject(obj):
137     hint = None
138     islist = False
139     try:
140         hint = obj[OSRF_JSON_CLASS_KEY]
141         obj = obj[OSRF_JSON_PAYLOAD_KEY]
142     except: pass
143     if isinstance(obj,list):
144         islist = True
145         for i in range(len(obj)):
146             obj[i] = parseNetObject(obj[i])
147     else: 
148         if isinstance(obj,dict):
149             for k,v in obj.iteritems():
150                 obj[k] = parseNetObject(v)
151
152     if hint: # Now, "bless" the object into an osrfNetworkObject
153         estr = 'obj = osrfNetworkObject.%s(obj)' % hint
154         try:
155             exec(estr)
156         except AttributeError:
157             # this object has not been registered, shove it into the default container
158             obj = osrfNetworkObject.__unknown(obj)
159
160     return obj;
161
162
163
164 def osrfObjectToXML(obj):
165     """ Returns the XML representation of an internal object."""
166     chars = []
167     __osrfObjectToXML(obj, chars)
168     return ''.join(chars)
169
170 def __osrfObjectToXML(obj, chars):
171     """ Turns an internal object into OpenSRF XML """
172
173     if obj is None:
174         chars.append('<null/>')
175         return
176
177     if isinstance(obj, unicode) or isinstance(obj, str):
178         chars.append('<string>%s</string>' % saxutils.escape(obj))
179         return
180
181     if isinstance(obj, int)  or isinstance(obj, long):
182         chars.append('<number>%d</number>' % obj)
183         return
184
185     if isinstance(obj, float):
186         chars.append('<number>%f</number>' % obj)
187         return
188
189     classHint = None
190
191     if isinstance(obj, osrfNetworkObject): 
192
193         registry = obj.getRegistry()
194         data = obj.getData()
195         hint = saxutils.escape(registry.hint)
196
197         if registry.wireProtocol == 'array':
198             chars.append("<array class_hint='%s'>" % hint)
199             for k in registry.keys:
200                 __osrfObjectToXML(data.get(k), chars)
201             chars.append('</array>')
202
203         else:
204             if registry.wireProtocol == 'hash':
205                 chars.append("<object class_hint='%s'>" % hint)
206                 for k,v in data.items():
207                     chars.append("<element key='%s'>" % saxutils.escape(k))
208                     __osrfObjectToXML(v, chars)
209                     chars.append('</element>')
210                 chars.append('</object>')
211                 
212
213     if isinstance(obj, list):
214         chars.append('<array>')
215         for i in obj:
216             __osrfObjectToXML(i, chars)
217         chars.append('</array>')
218         return
219
220     if isinstance(obj, dict):
221         chars.append('<object>')
222         for k,v in obj.items():
223             chars.append("<element key='%s'>" % saxutils.escape(k))
224             __osrfObjectToXML(v, chars)
225             chars.append('</element>')
226         chars.append('</object>')
227         return
228
229     if isinstance(obj, bool):
230         val = 'false'
231         if obj: val = 'true'
232         chars.append("<boolean value='%s'/>" % val)
233         return
234