2 Parses an Evergreen fieldmapper IDL file and builds a global registry of
3 objects representing that IDL.
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
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
20 class IDLException(osrf.ex.OSRFException):
23 class IDLParser(object):
25 # ------------------------------------------------------------
26 # static methods and variables for managing a global parser
27 # ------------------------------------------------------------
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
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:
44 idl_path = osrf.ses.ClientSession.atomic_request(
45 OILS_APP_ACTOR, 'opensrf.open-ils.fetch_idl.file')
46 parser.set_IDL(idl_path)
48 IDLParser._global_parser = parser
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
56 return IDLParser.get_parser().IDLObject[class_name]
58 # ------------------------------------------------------------
60 # ------------------------------------------------------------
65 def set_IDL(self, file):
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.
73 attr = node.attributes.get(name)
79 """Parses the IDL file and builds class, field, and link objects"""
81 # in case we're calling parse_IDL directly
82 if not IDLParser._global_parser:
83 IDLParser._global_parser = self
85 doc = xml.dom.minidom.parse(self.idlFile)
86 root = doc.documentElement
88 for child in root.childNodes:
90 if child.nodeType == child.ELEMENT_NODE and child.nodeName == 'class':
92 # -----------------------------------------------------------------------
93 # 'child' is the main class node for a fieldmapper class.
94 # It has 'fields' and 'links' nodes as children.
95 # -----------------------------------------------------------------------
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),
107 self.IDLObject[obj.name] = obj
109 fields = [f for f in child.childNodes if f.nodeName == 'fields']
110 links = [f for f in child.childNodes if f.nodeName == 'links']
112 fields = self.parse_fields(obj, fields[0])
114 self.parse_links(obj, links[0])
116 osrf.net_obj.register_hint(obj.name, [f.name for f in fields], 'array')
121 def parse_links(self, idlobj, links):
123 for link in [l for l in links.childNodes if l.nodeName == 'link']:
125 field = idlobj.get_field(self._get_attr(link, 'field')),
126 reltype = self._get_attr(link, 'reltype'),
127 key = self._get_attr(link, 'key'),
128 map = self._get_attr(link, 'map'),
129 class_ = self._get_attr(link, 'class')
131 idlobj.links.append(obj)
134 def parse_fields(self, idlobj, fields):
135 """Takes the fields node and parses the included field elements"""
137 idlobj.primary = self._get_attr(fields, 'oils_persist:primary', OILS_NS_PERSIST)
138 idlobj.sequence = self._get_attr(fields, 'oils_persist:sequence', OILS_NS_PERSIST)
141 for field in [l for l in fields.childNodes if l.nodeName == 'field']:
143 name = self._get_attr(field, 'name')
145 if name in ['isnew', 'ischanged', 'isdeleted']:
152 virtual = self._get_attr(field, 'oils_persist:virtual', OILS_NS_PERSIST),
153 label = self._get_attr(field, 'reporter:label', OILS_NS_REPORTER),
154 rpt_datatype = self._get_attr(field, 'reporter:datatype', OILS_NS_REPORTER),
155 rpt_select = self._get_attr(field, 'reporter:selector', OILS_NS_REPORTER),
156 primitive = self._get_attr(field, 'oils_persist:primitive', OILS_NS_PERSIST)
159 idlobj.fields.append(obj)
160 idlobj.field_map[obj.name] = obj
163 for name in ['isnew', 'ischanged', 'isdeleted']:
164 obj = IDLField(idlobj,
169 idlobj.fields.append(obj)
176 class IDLClass(object):
177 def __init__(self, name, **kwargs):
179 self.controller = kwargs.get('controller')
180 self.fieldmapper = kwargs.get('fieldmapper')
181 self.virtual = kwargs.get('virtual')
182 self.label = kwargs.get('label')
183 self.tablename = kwargs.get('tablename')
184 self.primary = kwargs.get('primary')
185 self.sequence = kwargs.get('sequence')
190 if self.virtual and self.virtual.lower() == 'true':
196 ''' Stringify the parsed IDL ''' # TODO: improve the format/content
199 s += "%s [%s] %s\n" % (self.label, self.name, self.tablename)
202 for f in self.fields:
204 if idx < 10: s += " "
210 def get_field(self, field_name):
212 return self.field_map[field_name]
214 msg = "No field '%s' in IDL class '%s'" % (field_name, self.name)
215 osrf.log.log_warn(msg)
216 #raise IDLException(msg)
218 class IDLField(object):
219 def __init__(self, idl_class, **kwargs):
221 @param idl_class The IDLClass object which owns this field
223 self.idl_class = idl_class
224 self.name = kwargs.get('name')
225 self.label = kwargs.get('label')
226 self.rpt_datatype = kwargs.get('rpt_datatype')
227 self.rpt_select = kwargs.get('rpt_select')
228 self.primitive = kwargs.get('primitive')
229 self.virtual = kwargs.get('virtual')
230 self.position = kwargs.get('position')
232 if self.virtual and self.virtual.lower() == 'true':
238 ''' Format as field name and data type, plus linked class for links. '''
240 if self.rpt_datatype:
241 s += " [" + self.rpt_datatype
242 if self.rpt_datatype == 'link':
243 link = [ l for l in self.idl_class.links if l.field.name == self.name ]
244 if len(link) > 0 and link[0].class_:
245 s += " @%s" % link[0].class_
250 class IDLLink(object):
251 def __init__(self, field, **kwargs):
253 @param field The IDLField object this link references
256 self.reltype = kwargs.get('reltype')
257 self.key = kwargs.get('key')
258 self.map = kwargs.get('map')
259 self.class_ = kwargs.get('class_')