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 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):
21 """Exception thrown when parsing the IDL file"""
24 class IDLParser(object):
25 """Evergreen fieldmapper IDL file parser"""
27 # ------------------------------------------------------------
28 # static methods and variables for managing a global parser
29 # ------------------------------------------------------------
34 ''' Returns the global IDL parser object '''
35 if IDLParser._global_parser is None:
36 raise IDLException("IDL has not been parsed")
37 return IDLParser._global_parser
41 ''' Finds the path to the IDL file from the OpenSRF settings
42 server, parses the IDL file, and uses the parsed data as
43 the global IDL repository '''
44 if IDLParser._global_parser is None:
46 idl_path = osrf.ses.ClientSession.atomic_request(
47 OILS_APP_ACTOR, 'opensrf.open-ils.fetch_idl.file')
48 parser.set_idl(idl_path)
50 IDLParser._global_parser = parser
53 def get_class(class_name):
54 ''' Returns the IDLClass object with the given
55 network hint / IDL class name.
56 @param The class ID from the IDL
58 return IDLParser.get_parser().idl_object[class_name]
60 # ------------------------------------------------------------
62 # ------------------------------------------------------------
65 """Initializes the IDL object"""
69 def set_IDL(self, idlfile):
70 """Deprecated non-PEP8 version of set_idl()"""
73 def set_idl(self, idlfile):
74 """Specifies the filename or file that contains the IDL"""
75 self.idl_file = idlfile
78 """Deprecated non-PEP8 version of parse_idl()"""
82 """Parses the IDL file and builds class, field, and link objects"""
84 # in case we're calling parse_idl directly
85 if not IDLParser._global_parser:
86 IDLParser._global_parser = self
88 doc = xml.dom.minidom.parse(self.idl_file)
89 root = doc.documentElement
91 for child in root.childNodes:
93 if child.nodeType == child.ELEMENT_NODE and child.nodeName == 'class':
95 # -----------------------------------------------------------------------
96 # 'child' is the main class node for a fieldmapper class.
97 # It has 'fields' and 'links' nodes as children.
98 # -----------------------------------------------------------------------
102 controller = _attr(child, 'controller'),
103 fieldmapper = _attr(child, 'oils_obj:fieldmapper', OILS_NS_OBJ),
104 virtual = _attr(child, 'oils_persist:virtual', OILS_NS_PERSIST),
105 label = _attr(child, 'reporter:label', OILS_NS_REPORTER),
106 tablename = _attr(child, 'oils_persist:tablename', OILS_NS_PERSIST),
107 field_safe = _attr(child, 'oils_persist:field_safe', OILS_NS_PERSIST),
110 self.idl_object[obj.name] = obj
112 fields = [f for f in child.childNodes if f.nodeName == 'fields']
113 links = [f for f in child.childNodes if f.nodeName == 'links']
115 fields = _parse_fields(obj, fields[0])
117 _parse_links(obj, links[0])
119 osrf.net_obj.register_hint(
120 obj.name, [f.name for f in fields], 'array'
126 class IDLClass(object):
127 """Represents a class in the fieldmapper IDL"""
129 def __init__(self, name, **kwargs):
131 self.controller = kwargs.get('controller')
132 self.fieldmapper = kwargs.get('fieldmapper')
133 self.virtual = _to_bool(kwargs.get('virtual'))
134 self.label = kwargs.get('label')
135 self.tablename = kwargs.get('tablename')
136 self.primary = kwargs.get('primary')
137 self.sequence = kwargs.get('sequence')
138 self.field_safe = _to_bool(kwargs.get('field_safe'))
144 ''' Stringify the parsed IDL ''' # TODO: improve the format/content
147 idl += "%s [%s] %s\n" % (self.label, self.name, self.tablename)
150 for field in self.fields:
154 idl += str(field) + '\n'
159 def get_field(self, field_name):
160 """Return the specified field from the class"""
163 return self.field_map[field_name]
165 msg = "No field '%s' in IDL class '%s'" % (field_name, self.name)
166 osrf.log.log_warn(msg)
167 #raise IDLException(msg)
169 class IDLField(object):
170 """Represents a field in a class in the fieldmapper IDL"""
172 def __init__(self, idl_class, **kwargs):
174 @param idl_class The IDLClass object which owns this field
176 self.idl_class = idl_class
177 self.name = kwargs.get('name')
178 self.label = kwargs.get('label')
179 self.rpt_datatype = kwargs.get('rpt_datatype')
180 self.rpt_select = kwargs.get('rpt_select')
181 self.primitive = kwargs.get('primitive')
182 self.virtual = kwargs.get('virtual')
183 self.position = kwargs.get('position')
185 if self.virtual and str(self.virtual).lower() == 'true':
191 ''' Format as field name and data type, plus linked class for links. '''
193 if self.rpt_datatype:
194 field += " [" + self.rpt_datatype
195 if self.rpt_datatype == 'link':
197 l for l in self.idl_class.links
198 if l.field.name == self.name
200 if len(link) > 0 and link[0].class_:
201 field += " @%s" % link[0].class_
206 class IDLLink(object):
207 """Represents a link between objects defined in the IDL"""
209 def __init__(self, field, **kwargs):
211 @param field The IDLField object this link references
214 self.reltype = kwargs.get('reltype')
215 self.key = kwargs.get('key')
216 self.map = kwargs.get('map')
217 self.class_ = kwargs.get('class_')
219 def _attr(node, name, namespace=None):
220 """ Find the attribute value on a given node
221 Namespace is ignored for now;
222 not sure if minidom has namespace support.
224 attr = node.attributes.get(name)
226 return attr.nodeValue
229 def _parse_links(idlobj, links):
230 """Parses the links between objects defined in the IDL"""
232 for link in [l for l in links.childNodes if l.nodeName == 'link']:
234 field = idlobj.get_field(_attr(link, 'field')),
235 reltype = _attr(link, 'reltype'),
236 key = _attr(link, 'key'),
237 map = _attr(link, 'map'),
238 class_ = _attr(link, 'class')
240 idlobj.links.append(obj)
242 def _parse_fields(idlobj, fields):
243 """Takes the fields node and parses the included field elements"""
245 idlobj.primary = _attr(fields, 'oils_persist:primary', OILS_NS_PERSIST)
246 idlobj.sequence = _attr(fields, 'oils_persist:sequence', OILS_NS_PERSIST)
249 for field in [l for l in fields.childNodes if l.nodeName == 'field']:
251 name = _attr(field, 'name')
253 if name in ['isnew', 'ischanged', 'isdeleted']:
260 virtual = _attr(field, 'oils_persist:virtual', OILS_NS_PERSIST),
261 label = _attr(field, 'reporter:label', OILS_NS_REPORTER),
262 rpt_datatype = _attr(field, 'reporter:datatype', OILS_NS_REPORTER),
263 rpt_select = _attr(field, 'reporter:selector', OILS_NS_REPORTER),
264 primitive = _attr(field, 'oils_persist:primitive', OILS_NS_PERSIST)
267 idlobj.fields.append(obj)
268 idlobj.field_map[obj.name] = obj
271 for name in ['isnew', 'ischanged', 'isdeleted']:
272 obj = IDLField(idlobj,
277 idlobj.fields.append(obj)
283 """Converts a string from the DOM into a boolean value. """
285 if field and str(field).lower() == 'true':