diff -r 87cfa131b535 -r e7e0ae78773e configurationengine/source/cone/confml/persistentconfml.py --- a/configurationengine/source/cone/confml/persistentconfml.py Fri Mar 12 08:30:17 2010 +0200 +++ b/configurationengine/source/cone/confml/persistentconfml.py Tue Aug 10 14:29:28 2010 +0300 @@ -13,9 +13,9 @@ # # Description: # +import os import re import logging -import xml.parsers.expat try: from cElementTree import ElementTree except ImportError: @@ -27,8 +27,8 @@ except ImportError: from xml.etree import ElementTree -""" cone specific imports """ -from cone.public import persistence, exceptions, api, utils, container +# cone specific imports +from cone.public import persistence, exceptions, api, utils, container, parsecontext from cone.confml import model CONFIGURATION_NAMESPACES = ["http://www.s60.com/xml/confml/2","http://www.s60.com/xml/confml/1"] @@ -42,12 +42,29 @@ etree = ConfmlWriter().dumps(obj) if indent: persistence.indent(etree) - return ElementTree.tostring(etree) + result = ElementTree.tostring(etree) + + # To make the output the same in linux and windows + # (needed to make testing easier) + if os.linesep != '\r\n': + result = result.replace(os.linesep, '\r\n') + + return result def loads(xml): return ConfmlReader().loads(xml) +def add_parse_warning(msg, line, type='model.confml'): + parsecontext.get_confml_context().handle_problem( + api.Problem(msg, + severity = api.Problem.SEVERITY_WARNING, + type = type, + line = line)) +def add_unknown_element_warning(elem): + add_parse_warning("Unknown element '%s'" % elem.tag, + utils.etree.get_lineno(elem), + type='model.confml.unknown_element') class ConfmlWriter(persistence.ConeWriter): """ @@ -69,7 +86,11 @@ @param xml: The xml which to read. reads only the first object. """ reader = get_reader_for_elem("configuration") - etree = utils.etree.fromstring(xmlstr) + try: + etree = utils.etree.fromstring(xmlstr) + except exceptions.XmlParseError, e: + e.problem_type = 'xml.confml' + raise e return reader.loads(etree) @@ -105,7 +126,13 @@ elem.set("xmlns:xlink",self.xlink_namespace) if self.schema_namespace: elem.set("xmlns:xs",self.schema_namespace) - elem.set("name",obj.get_name()) + if obj.get_name(): + elem.set("name", obj.get_name()) + if obj.version != None: + elem.set("version", obj.version) + if obj.get_id(): + elem.set('id', obj.get_id()) + for child in obj._objects(): """ Make sure that the object is mapped to an object in this model """ mobj = child._get_mapper('confml').map_object(child) @@ -137,8 +164,11 @@ self.schema_namespaces = SCHEMA_NAMESPACES def loads(self, etree): - configuration = model.ConfmlConfiguration("") - configuration.set_name(etree.get("name") or 'unknown') + configuration = model.ConfmlConfiguration(name=etree.get("name"), + id=etree.get("id"), + version=etree.get("version")) + configuration.lineno = utils.etree.get_lineno(etree) + # configuration.set_name(etree.get("name") or 'unknown') configuration.set_ref(etree.get("name") or 'unknown') for elem in etree.getchildren(): # At the moment we ignore the namespace of elements @@ -149,7 +179,7 @@ if obj: configuration.add(obj) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue return configuration @@ -202,6 +232,7 @@ def loads(self,etree): metaelem = model.ConfmlMeta() + metaelem.lineno = utils.etree.get_lineno(etree) for elem in etree.getchildren(): (namespace,elemname) = get_elemname(elem.tag) attributes = {} @@ -250,6 +281,7 @@ def loads(self,elem): desc = model.ConfmlDescription(elem.text) + desc.lineno = utils.etree.get_lineno(elem) return desc class ConfigurationProxyWriter(ConfmlWriter): @@ -295,8 +327,9 @@ """ @param elem: The xml include elem """ - - return api.ConfigurationProxy(self.parse_include(elem)) + proxy = api.ConfigurationProxy(self.parse_include(elem)) + # proxy.lineno = utils.etree.get_lineno(elem) + return proxy def parse_include(self,include): #print "Found include %s" % include.get('href').replace('#/','') @@ -321,10 +354,17 @@ @param obj: The Configuration object """ elem = ElementTree.Element('feature', - {'ref' : obj.get_ref(), - 'name' : obj.get_name()}) + {'ref' : obj.get_ref()}) + if obj.get_name(): + elem.set('name', obj.get_name()) if obj.get_type(): elem.set('type', obj.get_type()) + if obj.get_id() != None: + elem.set('id', obj.get_id()) + if obj.get_relevant() != None: + elem.set('relevant', obj.get_relevant()) + if obj.get_constraint() != None: + elem.set('constraint', obj.get_constraint()) for child in obj._objects(): """ Make sure that the object is mapped to an object in this model """ mobj = child._get_mapper('confml').map_object(child) @@ -360,7 +400,14 @@ feature = model.ConfmlFeature(elem.get('ref')) if elem.get('name'): feature.set_name(elem.get('name')) + if elem.get('id') != None: + feature.set_id(elem.get('id')) + if elem.get('relevant') != None: + feature.set_relevant(elem.get('relevant')) + if elem.get('constraint') != None: + feature.set_constraint(elem.get('constraint')) feature.set_type(type) + feature.lineno = utils.etree.get_lineno(elem) for elem in elem.getchildren(): # At the moment we ignore the namespace of elements (namespace,elemname) = get_elemname(elem.tag) @@ -369,7 +416,7 @@ obj = reader.loads(elem) feature.add(obj) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue return feature @@ -396,6 +443,9 @@ if obj.get_value() is not None: objdict['value'] = obj.get_value() if obj.map is not None: objdict['map'] = obj.map if obj.relevant is not None: objdict['relevant'] = obj.relevant + if obj.display_name is not None: objdict['displayName'] = obj.display_name + if obj.map_value is not None: objdict['mapValue'] = obj.map_value + elem = ElementTree.Element('option', objdict) return elem @@ -422,11 +472,16 @@ name = elem.get('name') value = elem.get('value') optmap = elem.get('map') + if value == None and optmap == None: logging.getLogger('cone').warning("Encountered option with no value") option = None else: - option = api.Option(name, value, map=optmap, relevant=elem.get('relevant')) + option = api.Option(name, value, map=optmap, + relevant=elem.get('relevant'), + map_value=elem.get('mapValue'), + display_name=elem.get('displayName')) + option.lineno = utils.etree.get_lineno(elem) return option @@ -471,6 +526,7 @@ """ href = elem.get('{%s}href' % XLINK_NAMESPACES[0]) obj = model.ConfmlIcon(href) + obj.lineno = utils.etree.get_lineno(elem) return obj @@ -481,7 +537,7 @@ Class method to determine if this ConfmlWriter supports writing of the given class name """ - if classname=="ConfmlProperty": + if classname=="Property": return True else: return False @@ -518,7 +574,8 @@ """ @param elem: The xml include elem """ - option = model.ConfmlProperty(name=elem.get('name'),value=elem.get('value'), unit=elem.get('unit')) + option = api.Property(name=elem.get('name'),value=elem.get('value'), unit=elem.get('unit')) + option.lineno = utils.etree.get_lineno(elem) return option @@ -574,6 +631,7 @@ elem_name = utils.xml.split_tag_namespace(elem.tag)[1] facet_class = self.MAPPING[elem_name] obj = facet_class(elem.get('value')) + obj.lineno = utils.etree.get_lineno(elem) return obj @@ -584,9 +642,8 @@ Class method to determine if this ConfmlWriter supports writing of the given class name """ - if classname=="Data": - return True - if classname=="DataContainer": + if classname=="Data" or \ + classname=="DataContainer": return True else: return False @@ -597,14 +654,16 @@ """ # Create a data hierarchy of the elem = ElementTree.Element(obj.get_ref()) - if hasattr(obj,'get_value') and obj.get_value() and not obj.get_map(): + if hasattr(obj,'get_map') and obj.get_map() is not None: + elem.set('map', obj.get_map()) + elif hasattr(obj,'get_value') and obj.get_value() is not None: elem.text = obj.get_value() - elif hasattr(obj,'get_map') and obj.get_map(): - elem.set('map', obj.get_map()) if hasattr(obj,'template') and obj.template == True: elem.set('template','true') if hasattr(obj,'policy') and obj.policy != '': elem.set('extensionPolicy',obj.policy) + if hasattr(obj,'empty') and obj.empty == True: + elem.set('empty','true') for child in obj._objects(): writer = DataWriter() childelem = writer.dumps(child) @@ -634,13 +693,14 @@ (namespace,elemname) = get_elemname(elem.tag) obj = api.DataContainer(elemname, container=True) + obj.lineno = utils.etree.get_lineno(elem) for elem in elem.getchildren(): try: reader = ElemReader(attr='data') childobj = reader.loads(elem) obj.add(childobj) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue return obj @@ -663,8 +723,9 @@ @param obj: The Configuration object """ elem = ElementTree.Element('view', - {'id' : obj.get_ref(), - 'name' : obj.get_name()}) + {'name' : obj.get_name()}) + if obj.id != None: + elem.set('id', obj.id) for child in obj._objects(): """ Make sure that the object is mapped to an object in this model """ mobj = child._get_mapper('confml').map_object(child) @@ -696,6 +757,7 @@ vid = elem.get('id') name = elem.get('name') view = model.ConfmlView(name, id=vid) + view.lineno = utils.etree.get_lineno(elem) for elem in elem.getchildren(): # At the moment we ignore the namespace of elements (namespace,elemname) = get_elemname(elem.tag) @@ -704,7 +766,7 @@ obj = reader.loads(elem) view.add(obj) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue return view @@ -727,6 +789,9 @@ """ elem = ElementTree.Element('group', {'name' : obj.get_name()}) + if obj.get_id(): + elem.set('id', obj.get_id()) + for child in obj._objects(): """ Make sure that the object is mapped to an object in this model """ mobj = child._get_mapper('confml').map_object(child) @@ -758,6 +823,9 @@ gname = elem.get('name') gref = utils.resourceref.to_dref(gname).replace('.','_') group = model.ConfmlGroup(gref, name=gname) + group.lineno = utils.etree.get_lineno(elem) + if elem.get('id'): + group.set_id(elem.get('id')) for elem in elem.getchildren(): # At the moment we ignore the namespace of elements (namespace,elemname) = get_elemname(elem.tag) @@ -767,58 +835,11 @@ if obj != None: group.add(obj) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue return group -class GroupSettingWriter(ConfmlWriter): - """ - """ - @classmethod - def supported_class(cls, classname): - """ - Class method to determine if this ConfmlWriter supports writing - of the given class name - """ - if classname=="FeatureLink": - return True - else: - return False - - def dumps(self, obj): - """ - @param obj: The Configuration object - """ - ref = obj.fqr.replace('.','/') - elem = ElementTree.Element('setting', - {'ref' : ref}) - return elem - -class GroupSettingReader(ConfmlReader): - """ - """ - @classmethod - def supported_elem(cls, elemname, parent=None): - """ - Class method to determine if this ConfmlWriter supports reading - of the given elem name - """ - if elemname=='setting' and parent=='group': - return True - else: - return False - - def loads(self, elem): - """ - @param elem: The xml include elem - """ - ref = elem.get('ref') or '' - ref = ref.replace('/','.') - return api.FeatureLink(ref) - - - class ConfmlSettingWriter(ConfmlWriter): @classmethod def supported_class(cls, classname): @@ -837,6 +858,7 @@ classname=="ConfmlTimeSetting" or \ classname=="ConfmlDateTimeSetting" or \ classname=="ConfmlDurationSetting" or \ + classname=="ConfmlHexBinarySetting" or \ classname=="ConfmlFileSetting" or \ classname=="ConfmlFolderSetting" or \ classname=="FeatureSequence" or \ @@ -850,10 +872,20 @@ @param obj: The Configuration object """ elem = ElementTree.Element('setting', - {'ref' : obj.get_ref(), - 'name' : obj.get_name()}) + {'ref' : obj.get_ref()}) + self._set_setting_properties(elem, obj) + return elem + + def _set_setting_properties(self, elem, obj): + """ + Set the setting properties for the given elm from the given feature object + """ + if obj.get_name(): + elem.set('name', obj.get_name()) if obj.type: elem.set('type', obj.get_type()) + if obj.get_id(): + elem.set('id', obj.get_id()) if hasattr(obj,'minOccurs'): elem.set('minOccurs', str(obj.minOccurs)) if hasattr(obj,'maxOccurs'): @@ -870,6 +902,8 @@ elem.set('mapKey', str(obj.mapKey)) if hasattr(obj,'mapValue') and obj.mapValue is not None: elem.set('mapValue', str(obj.mapValue)) + if hasattr(obj,'displayName') and obj.displayName is not None: + elem.set('displayName', str(obj.displayName)) for child in obj._objects(): """ Make sure that the object is mapped to an object in this model """ @@ -878,7 +912,6 @@ childelem = writer.dumps(child) if childelem != None: elem.append(childelem) - return elem class ConfmlSettingReader(ConfmlReader): @@ -891,7 +924,7 @@ of the given elem name """ if parent and not (parent=='feature' or parent=='setting'): - return False + return False if elemname=='setting': return True else: @@ -905,7 +938,8 @@ if typedef == 'sequence': map_key = elem.get('mapKey') map_value = elem.get('mapValue') - feature = model.ConfmlSequenceSetting(elem.get('ref'), mapKey=map_key, mapValue=map_value) + display_name = elem.get('displayName') + feature = model.ConfmlSequenceSetting(elem.get('ref'), mapKey=map_key, mapValue=map_value, displayName=display_name) elif typedef == 'int': feature = model.ConfmlIntSetting(elem.get('ref')) elif typedef == 'boolean': @@ -930,14 +964,25 @@ feature = model.ConfmlDateTimeSetting(elem.get('ref')) elif typedef == 'duration': feature = model.ConfmlDurationSetting(elem.get('ref')) + elif typedef == 'hexBinary': + feature = model.ConfmlHexBinarySetting(elem.get('ref')) else: # Handle the default setting as int type - feature = model.ConfmlSetting(elem.get('ref'), type=None) + feature = model.ConfmlSetting(elem.get('ref'), type=typedef) + feature.lineno = utils.etree.get_lineno(elem) + self._get_setting_properties(elem, feature) + return feature + def _get_setting_properties(self, elem, feature): + """ + Get the setting properties for the given feature from the given xml elem + """ if elem.get('name'): feature.set_name(elem.get('name')) + if elem.get('id'): + feature.set_id(elem.get('id')) if elem.get('minOccurs'): feature.minOccurs = int(elem.get('minOccurs')) if elem.get('maxOccurs'): @@ -960,12 +1005,63 @@ if obj != None: feature.add(obj,container.APPEND) else: - logging.getLogger('cone').warning("Invalid child %s in %s" % (elem,feature.name)) + add_parse_warning("Invalid child %s in %s" % (elem, feature.name), + utils.etree.get_lineno(elem)) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue - return feature + + +class GroupSettingWriter(ConfmlSettingWriter): + """ + """ + @classmethod + def supported_class(cls, classname): + """ + Class method to determine if this ConfmlWriter supports writing + of the given class name + """ + if classname=="FeatureLink" or \ + classname=="ConfmlFeatureLink": + return True + else: + return False + def dumps(self, obj): + """ + @param obj: The Configuration object + """ + ref = obj.fqr.replace('.','/') + elem = ElementTree.Element('setting', + {'ref' : ref}) + self._set_setting_properties(elem, obj) + return elem + + +class GroupSettingReader(ConfmlSettingReader): + """ + """ + @classmethod + def supported_elem(cls, elemname, parent=None): + """ + Class method to determine if this ConfmlWriter supports reading + of the given elem name + """ + if elemname=='setting' and parent=='group': + return True + else: + return False + + def loads(self, elem): + """ + @param elem: The xml include elem + """ + ref = elem.get('ref') or '' + ref = ref.replace('/','.') + feature_link = model.ConfmlFeatureLink(ref) + feature_link.lineno = utils.etree.get_lineno(elem) + self._get_setting_properties(elem, feature_link) + return feature_link class ConfmlLocalPathWriter(ConfmlWriter): @classmethod @@ -1007,7 +1103,9 @@ """ @param elem: The xml include elem """ - return model.ConfmlLocalPath(readOnly=elem.get('readOnly')) + obj = model.ConfmlLocalPath(readOnly=elem.get('readOnly')) + obj.lineno = utils.etree.get_lineno(elem) + return obj class ConfmlTargetPathWriter(ConfmlWriter): @@ -1050,7 +1148,9 @@ """ @param elem: The xml include elem """ - return model.ConfmlTargetPath(readOnly=elem.get('readOnly')) + obj = model.ConfmlTargetPath(readOnly=elem.get('readOnly')) + obj.lineno = utils.etree.get_lineno(elem) + return obj class DummyWriter(ConfmlWriter): @@ -1094,13 +1194,14 @@ (namespace,elemname) = get_elemname(elem.tag) obj = api.DataContainer(elemname, container=True) + obj.lineno = utils.etree.get_lineno(elem) for elem in elem.getchildren(): try: reader = ElemReader(attr='rfs') childobj = reader.loads(elem) obj.add(childobj) except exceptions.ConePersistenceError,e: - logging.getLogger('cone').warning("Could not parse element %s. Exception: %s" % (elem,e)) + add_unknown_element_warning(elem) continue return obj @@ -1118,10 +1219,15 @@ datavalue = None if len(list(elem)) == 0: datavalue = elem.text - datatemplate = elem.get('template') == 'true' or self.template - dataextensionpolicy = elem.get('extensionPolicy') or '' - datamap = elem.get('map') - obj = api.Data(ref=elemname,value=datavalue, template=datatemplate, attr=self.attr,policy=dataextensionpolicy,map=datamap) + obj = model.ConfmlData( + ref = elemname, + value = datavalue, + template = elem.get('template') == 'true' or self.template, + attr = self.attr, + policy = elem.get('extensionPolicy') or '', + map = elem.get('map'), + empty = elem.get('empty') == 'true') + obj.lineno = utils.etree.get_lineno(elem) for elem in elem.getchildren(): try: reader = ElemReader(**self.args) @@ -1152,13 +1258,13 @@ def get_reader_for_elem(elemname, parent=None): - for reader in ConfmlReader.__subclasses__(): + for reader in utils.all_subclasses(ConfmlReader): if reader.supported_elem(elemname,parent): return reader() raise exceptions.ConePersistenceError("No reader for given elem %s under %s found!" % (elemname, parent)) def get_writer_for_class(classname): - for writer in ConfmlWriter.__subclasses__(): + for writer in utils.all_subclasses(ConfmlWriter): if writer.supported_class(classname): return writer () raise exceptions.ConePersistenceError("No writer for given class found! %s" % classname)