]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/src/python/oils/utils/idl.py
Some python code is non-executable (script_x_check updated)
[working/Evergreen.git] / Open-ILS / src / python / oils / utils / idl.py
1 """
2 Parses an Evergreen fieldmapper IDL file and builds a global registry of
3 objects representing that IDL.
4
5 Typical usage:
6
7 >>> import osrf.system
8 >>> import oils.utils.idl
9 >>> osrf.system.connect('/openils/conf/opensrf_core.xml', 'config.opensrf')
10 >>> oils.utils.idl.IDLParser.parse()
11 >>> # 'bre' is a network registry hint, or class ID in the IDL file
12 ... print oils.utils.idl.IDLParser.get_class('bre').tablename
13 biblio.record_entry
14 """
15 import sys, string, xml.dom.minidom
16 #import osrf.net_obj, osrf.log, osrf.set, osrf.ex, osrf.ses
17 import osrf.net_obj, osrf.log, osrf.ex, osrf.ses
18 from oils.const import OILS_NS_OBJ, OILS_NS_PERSIST, OILS_NS_REPORTER, OILS_APP_ACTOR
19
20 class IDLException(osrf.ex.OSRFException):
21     pass
22
23 class IDLParser(object):
24
25     # ------------------------------------------------------------
26     # static methods and variables for managing a global parser
27     # ------------------------------------------------------------
28     _global_parser = None
29
30     @staticmethod
31     def get_parser():
32         ''' Returns the global IDL parser object '''
33         if IDLParser._global_parser is None:
34             raise IDLException("IDL has not been parsed")
35         return IDLParser._global_parser
36
37     @staticmethod
38     def parse():
39         ''' Finds the path to the IDL file from the OpenSRF settings 
40             server, parses the IDL file, and uses the parsed data as
41             the global IDL repository '''
42         if IDLParser._global_parser is None:
43             parser = IDLParser()
44             idl_path = osrf.ses.ClientSession.atomic_request(
45                 OILS_APP_ACTOR, 'opensrf.open-ils.fetch_idl.file')
46             parser.set_IDL(idl_path)
47             parser.parse_IDL()
48             IDLParser._global_parser = parser
49
50     @staticmethod
51     def get_class(class_name):
52         ''' Returns the IDLClass object with the given 
53             network hint / IDL class name.
54             @param The class ID from the IDL
55             '''
56         return IDLParser.get_parser().IDLObject[class_name]
57
58     # ------------------------------------------------------------
59     # instance methods
60     # ------------------------------------------------------------
61
62     def __init__(self):
63         self.IDLObject = {}
64
65     def set_IDL(self, file):
66         self.idlFile = file
67
68     def _get_attr(self, node, name, ns=None):
69         """ Find the attribute value on a given node 
70             Namespace is ignored for now.. 
71             not sure if minidom has namespace support.
72             """
73         attr = node.attributes.get(name)
74         if attr:
75             return attr.nodeValue
76         return None
77
78     def parse_IDL(self):
79         """Parses the IDL file and builds class, field, and link objects"""
80
81         # in case we're calling parse_IDL directly
82         if not IDLParser._global_parser:
83             IDLParser._global_parser = self
84
85         doc = xml.dom.minidom.parse(self.idlFile)
86         root = doc.documentElement
87
88         for child in root.childNodes:
89         
90             if child.nodeType == child.ELEMENT_NODE and child.nodeName == 'class':
91         
92                 # -----------------------------------------------------------------------
93                 # 'child' is the main class node for a fieldmapper class.
94                 # It has 'fields' and 'links' nodes as children.
95                 # -----------------------------------------------------------------------
96
97                 obj = IDLClass(
98                     self._get_attr(child, 'id'),
99                     controller = self._get_attr(child, 'controller'),
100                     fieldmapper = self._get_attr(child, 'oils_obj:fieldmapper', OILS_NS_OBJ),
101                     virtual = self._get_attr(child, 'oils_persist:virtual', OILS_NS_PERSIST),
102                     label = self._get_attr(child, 'reporter:label', OILS_NS_REPORTER),
103                     tablename = self._get_attr(child, 'oils_persist:tablename', OILS_NS_REPORTER),
104                 )
105
106
107                 self.IDLObject[obj.name] = obj
108
109                 fields = [f for f in child.childNodes if f.nodeName == 'fields']
110                 links = [f for f in child.childNodes if f.nodeName == 'links']
111
112                 fields = self.parse_fields(obj, fields[0])
113                 if len(links) > 0:
114                     self.parse_links(obj, links[0])
115
116                 osrf.net_obj.register_hint(obj.name, [f.name for f in fields], 'array')
117
118         doc.unlink()
119
120
121     def parse_links(self, idlobj, links):
122
123         for link in [l for l in links.childNodes if l.nodeName == 'link']:
124             obj = IDLLink(
125                 field = idlobj.get_field(self._get_attr(link, 'field')),
126                 rel_type = self._get_attr(link, 'rel_type'),
127                 key = self._get_attr(link, 'key'),
128                 map = self._get_attr(link, 'map')
129             )
130             idlobj.links.append(obj)
131
132
133     def parse_fields(self, idlobj, fields):
134         """Takes the fields node and parses the included field elements"""
135
136         idlobj.primary = self._get_attr(fields, 'oils_persist:primary', OILS_NS_PERSIST)
137         idlobj.sequence =  self._get_attr(fields, 'oils_persist:sequence', OILS_NS_PERSIST)
138
139         position = 0
140         for field in [l for l in fields.childNodes if l.nodeName == 'field']:
141
142             name = self._get_attr(field, 'name')
143
144             if name in ['isnew', 'ischanged', 'isdeleted']: 
145                 continue
146
147             obj = IDLField(
148                 idlobj,
149                 name = name,
150                 position = position,
151                 virtual = self._get_attr(field, 'oils_persist:virtual', OILS_NS_PERSIST),
152                 label = self._get_attr(field, 'reporter:label', OILS_NS_REPORTER),
153                 rpt_datatype = self._get_attr(field, 'reporter:datatype', OILS_NS_REPORTER),
154                 rpt_select = self._get_attr(field, 'reporter:selector', OILS_NS_REPORTER),
155                 primitive = self._get_attr(field, 'oils_persist:primitive', OILS_NS_PERSIST)
156             )
157
158             idlobj.fields.append(obj)
159             position += 1
160
161         for name in ['isnew', 'ischanged', 'isdeleted']: 
162             obj = IDLField(idlobj, 
163                 name = name, 
164                 position = position, 
165                 virtual = 'true'
166             )
167             idlobj.fields.append(obj)
168             position += 1
169
170         return idlobj.fields
171
172
173
174 class IDLClass(object):
175     def __init__(self, name, **kwargs):
176         self.name = name
177         self.controller = kwargs.get('controller')
178         self.fieldmapper = kwargs.get('fieldmapper')
179         self.virtual = kwargs.get('virtual')
180         self.label = kwargs.get('label')
181         self.tablename = kwargs.get('tablename')
182         self.primary = kwargs.get('primary')
183         self.sequence = kwargs.get('sequence')
184         self.fields = []
185         self.links = []
186
187         if self.virtual and self.virtual.lower() == 'true':
188             self.virtual = True
189         else:
190             self.virtual = False
191
192     def get_field(self, field_name):
193         try:
194             return [f for f in self.fields if f.name == field_name][0]
195         except:
196             msg = "No field '%s' in IDL class '%s'" % (field_name, self.name)
197             osrf.log.log_warn(msg)
198             #raise IDLException(msg)
199
200 class IDLField(object):
201     def __init__(self, idl_class, **kwargs):
202         '''
203             @param idl_class The IDLClass object which owns this field
204         '''
205         self.idl_class = idl_class
206         self.name = kwargs.get('name')
207         self.label = kwargs.get('label')
208         self.rpt_datatype = kwargs.get('rpt_datatype')
209         self.rpt_select = kwargs.get('rpt_select')
210         self.primitive = kwargs.get('primitive')
211         self.virtual = kwargs.get('virtual')
212         self.position = kwargs.get('position')
213
214         if self.virtual and self.virtual.lower() == 'true':
215             self.virtual = True
216         else:
217             self.virtual = False
218
219
220 class IDLLink(object):
221     def __init__(self, field, **kwargs):
222         '''
223             @param field The IDLField object this link references
224         '''
225         self.field = field
226         self.rel_type = kwargs.get('rel_type')
227         self.key = kwargs.get('key')
228         self.map = kwargs.get('map')
229
230