diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/plugins/common/ConeContentPlugin/contentplugin/contentmlparser.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configurationengine/source/plugins/common/ConeContentPlugin/contentplugin/contentmlparser.py Thu Mar 11 17:04:37 2010 +0200 @@ -0,0 +1,452 @@ +# +# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +# All rights reserved. +# This component and the accompanying materials are made available +# under the terms of "Eclipse Public License v1.0" +# which accompanies this distribution, and is available +# at the URL "http://www.eclipse.org/legal/epl-v10.html". +# +# Initial Contributors: +# Nokia Corporation - initial contribution. +# +# Contributors: +# +# Description: +# +## +# @author Teemu Rytkonen +''' +A plugin implementation for content selection from ConfigurationLayers. +''' + + +import re +import os +import sys +import logging +import shutil +import copy +import xml.parsers.expat + +try: + from cElementTree import ElementTree +except ImportError: + try: + from elementtree import ElementTree + except ImportError: + try: + from xml.etree import cElementTree as ElementTree + except ImportError: + from xml.etree import ElementTree + +import __init__ + +from cone.public import exceptions,plugin,utils,api + +class ContentOutput(object): + def __init__(self, **kwargs): + self._dir = kwargs.get('dir',None) + self._file = kwargs.get('file',None) + self.flatten = kwargs.get('flatten',False) + self.inputs = kwargs.get('inputs', []) + self.configuration = None + + def set_configuration(self, configuration): + self.configuration = configuration + for input in self.inputs: + input.configuration = self.configuration + + def get_configuration(self, configuration): + self.configuration = configuration + + def path_convert(self, path): + (drive, tail) = os.path.splitdrive(path) + return tail.lstrip('\\/') + + def get_dir(self): + if self.configuration and ConfmlRefs.is_confml_ref(self._dir): + parts = self._dir.split(ConfmlRefs.ref_separator) + for (index, part) in enumerate(parts): + if ConfmlRefs.is_confml_ref(part): + ref = ConfmlRefs.get_confml_ref(part) + parts[index] = self.configuration.get_default_view().get_feature(ref).value + parts[index] = self.path_convert(parts[index]) + else: + parts[index] = part + return (os.sep).join(parts) + else: + return self.path_convert(self._dir) + + def set_dir(self, dir): + self._dir = dir + + def get_file(self): + if self.configuration and self._file != None and ConfmlRefs.is_confml_ref(self._file): + parts = self._file.split(ConfmlRefs.ref_separator) + for (index, part) in enumerate(parts): + if ConfmlRefs.is_confml_ref(part): + ref = ConfmlRefs.get_confml_ref(part) + parts[index] = self.configuration.get_default_view().get_feature(ref).value + parts[index] = self.path_convert(parts[index]) + else: + parts[index] = part + return (os.sep).join(parts) + else: + return self._file + + def set_file(self, file): + self._file = file + + dir = property(get_dir, set_dir) + file = property(get_file, set_file) + + def get_refs(self): + refs = [] + for input in self.inputs: + refs.extend(input.get_refs()) + return refs + + +class ContentInput(object): + def __init__(self, **kwargs): + self._dir = kwargs.get('dir',None) + self._file = kwargs.get('file',None) + self._include = kwargs.get('include', {}) + self._exclude = kwargs.get('exclude', {}) + self.configuration = None + + @property + def dir(self): + + if self.configuration and ConfmlRefs.is_confml_ref(self._dir): + cref = ConfmlRefs.get_confml_ref(self._dir) + return self.configuration.get_default_view().get_feature(cref).value + else: + return self._dir + + @property + def file(self): + + if self._file and self.configuration and ConfmlRefs.is_confml_ref(self._file): + cref = ConfmlRefs.get_confml_ref(self._file) + return self.configuration.get_default_view().get_feature(cref).value + else: + return self._file + + + def _dereference_dict(self, data): + if self.configuration: + # Make a deep copy of the data, or otherwise get_refs() will + # return the correct refs only on the first call, as the + # references are replaced here + data = copy.deepcopy(data) + + dview = self.configuration.get_default_view() + for key in data: + key_list = data.get(key) + for (index,elem) in enumerate(key_list): + if ConfmlRefs.is_confml_ref(elem): + cref = ConfmlRefs.get_confml_ref(elem) + try: + # change None value to empty string + cvalue = dview.get_feature(cref).value or '' + if utils.is_list(cvalue): + cvalue = ", ".join(cvalue) + key_list[index] = cvalue + except exceptions.NotFound: + logging.getLogger('cone.content').error("Feature ref '%s' in include key '%s' not found." % (cref,key)) + return data + + @property + def include(self): + return self._dereference_dict(self._include) + + @property + def exclude(self): + return self._dereference_dict(self._exclude) + + def get_filelist(self): + filelist = [] + if self.file: + filelist.append(self.file) + for elem in self.include.get('files',[]): + elem = elem.lower().split(',') + filelist += [selem.strip() for selem in elem] + return filelist + + def get_include_pattern(self): + return self.include.get('pattern',[''])[0] + + def get_exclude_pattern(self): + return self.exclude.get('pattern',[''])[0] + + def get_refs(self): + refs = [] + if self._dir is not None: + refs.extend(utils.extract_delimited_tokens(self._dir)) + if self._file is not None: + refs.extend(utils.extract_delimited_tokens(self._file)) + for value_list in self._include.itervalues(): + for value in value_list: + refs.extend(utils.extract_delimited_tokens(value)) + for value_list in self._exclude.itervalues(): + for value in value_list: + refs.extend(utils.extract_delimited_tokens(value)) + return refs + + +class ExternalContentInput(ContentInput): + def __init__(self, **kwargs): + super(ExternalContentInput,self).__init__(**kwargs) + +class ContentParserBase(object): + """ + Parses a single content implml file + """ + NAMESPACES = ['http://www.s60.com/xml/content/1'] + INCLUDE_ATTR = ['pattern'] + EXCLUDE_ATTR = ['pattern'] + def __init__(self): + self.namespaces = self.NAMESPACES + + def parse_phase(self,etree): + phase = "" + phase = etree.get('phase') + return phase + + def parse_desc(self,etree): + desc = "" + desc_elem = etree.find("{%s}desc" % self.namespaces[0]) + if desc_elem != None: + desc = desc_elem.text + return desc + + def parse_tags(self,etree): + tags = {} + for tag in etree.getiterator("{%s}tag" % self.namespaces[0]): + tagname = tag.get('name','') + tagvalue = tag.get('value') + values = tags.get(tagname,[]) + values.append(tagvalue) + tags[tagname] = values + return tags + + def parse_input_include(self,etree): + include_elem = etree.getiterator("{%s}include" % self.namespaces[0]) + include = {} + for f in include_elem: + for key in f.keys(): + # Add the attribute if it is found to include dict + include[key] = [] + include[key].append(f.get(key)) + return include + + def parse_input_exclude(self,etree): + elem = etree.getiterator("{%s}exclude" % self.namespaces[0]) + exclude = {} + for f in elem: + for key in f.keys(): + # Add the attribute if it is found + exclude[key] = [] + exclude[key].append(f.get(key)) + return exclude + +class Content1Parser(ContentParserBase): + """ + Parses a single content implml file + """ + NAMESPACES = ['http://www.s60.com/xml/content/1'] + def __init__(self): + super(ContentParserBase,self).__init__() + self.namespaces = self.NAMESPACES + + def parse_input(self,etree): + input_elem = etree.find("{%s}input" % self.namespaces[0]) + input_dir = "" + input_file = "" + if input_elem != None: + if input_elem.get('dir'): + input_dir = input_elem.get('dir') + if input_elem.get('file'): + input_dir = input_elem.get('file') + includes = self.parse_input_include(etree) + excludes = self.parse_input_exclude(etree) + return ContentInput(dir=input_dir, include=includes, exclude=excludes, file=input_file) + return None + + def parse_outputs(self,etree): + output_elem = etree.find("{%s}output" % self.namespaces[0]) + output_dir = "" + output_flatten = False + inputs = [] + if output_elem != None: + output_dir = output_elem.get('dir','') + output_flatten = output_elem.get('flatten','') == "true" + input = self.parse_input(etree) + if input: + inputs.append(input) + return [ContentOutput(dir=output_dir, flatten=output_flatten, inputs=inputs)] + +class Content2Parser(ContentParserBase): + """ + Parses a single content implml file + """ + NAMESPACES = ['http://www.s60.com/xml/content/2'] + def __init__(self): + super(ContentParserBase,self).__init__() + self.namespaces = self.NAMESPACES + + + def parse_input(self,input_elem): + input = None + input_dir = '' + input_file = '' + if input_elem != None: + if input_elem.get('dir'): + input_dir = input_elem.get('dir') + if input_elem.get('file'): + input_file= input_elem.get('file') + includes = self.parse_input_include(input_elem) + excludes = self.parse_input_exclude(input_elem) + input = ContentInput(dir=input_dir, include=includes, exclude=excludes, file=input_file) + return input + + def parse_external_input(self,input_elem): + input = None + input_dir = '' + if input_elem != None: + if input_elem.get('dir'): + input_dir = input_elem.get('dir') + includes = self.parse_input_include(input_elem) + excludes = self.parse_input_exclude(input_elem) + input = ExternalContentInput(dir=input_dir, include=includes, exclude=excludes) + return input + + def parse_outputs(self,etree): + outputs = [] + for output_elem in etree.getiterator("{%s}output" % self.namespaces[0]): + inputs = [] + output_dir = output_elem.get('dir','') + output_file = output_elem.get('file','') + output_flatten = output_elem.get('flatten','') == "true" + for input_elem in output_elem.getiterator("{%s}input" % self.namespaces[0]): + inputs.append(self.parse_input(input_elem)) + for input_elem in output_elem.getiterator("{%s}externalinput" % self.namespaces[0]): + inputs.append(self.parse_external_input(input_elem)) + outputs.append(ContentOutput(dir=output_dir, flatten=output_flatten, inputs=inputs, file=output_file)) + return outputs + + +class ContentImplReader(object): + """ + Parses a single content implml file + """ + PARSERS = {'http://www.s60.com/xml/content/1' : Content1Parser, + 'http://www.s60.com/xml/content/2' : Content2Parser} + def __init__(self): + self.desc = None + self.outputs = None + self.phase = None + + def fromstring(self, xml_as_string): + etree = ElementTree.fromstring(xml_as_string) + # Loop through parsers and try to find a match + (namespace,elemname) = get_elemname(etree.tag) + pclass = self.PARSERS.get(namespace, None) + self.parser = pclass() + self.desc = self.parser.parse_desc(etree) + self.outputs = self.parser.parse_outputs(etree) + self.phase = self.parser.parse_phase(etree) + self.tags = self.parser.parse_tags(etree) + return + +namespace_pattern = re.compile("{(.*)}(.*)") +nonamespace_pattern = re.compile("(.*)") + +def get_elemname(tag): + + ns = namespace_pattern.match(tag) + nn = nonamespace_pattern.match(tag) + if ns: + namespace = ns.group(1) + elemname = ns.group(2) + return (namespace,elemname) + elif nn: + namespace = "" + elemname = nn.group(1) + return (namespace,elemname) + else: + raise exceptions.ParseError("Could not parse tag %s" % tag) + +class ConfmlRefs(object): + + ref_pattern = re.compile('^\$\{(.*)\}$') + cref_pattern = re.compile('.+\..+') + ref_separator = '/' + + @classmethod + def is_ref_like(cls, variableref): + """ + + Returns true if the given variable represents a ref + """ + return cls.cref_pattern.match(variableref) != None + + @classmethod + def is_confml_ref(cls, variableref): + """ + + Returns true if the given variable ref is a confml reference + """ + + pos = variableref.find(cls.ref_separator) + if pos == -1: + return cls.ref_pattern.match(variableref) != None + else: + return cls.is_confml_refs(variableref) + + @classmethod + def is_confml_refs(cls, variableref): + """ + + Returns true if the given variable ref is a confml reference + """ + ret = False + parts = variableref.split(cls.ref_separator) + for p in parts: + if cls.is_confml_ref(p) == True: + ret = True + return ret + + + @classmethod + def get_confml_ref(cls, variableref): + """ + + Returns true if the given variable ref is a confml reference + """ + pos = variableref.find(cls.ref_separator) + if pos == -1: + matchref = cls.ref_pattern.match(variableref) + if matchref: + return matchref.group(1) + else: + return cls.get_confml_refs(variableref) + + @classmethod + def get_confml_refs(cls, variableref): + """ + + Returns an array of confml refs based on variableref + """ + parts = variableref.split(cls.ref_separator) + ret = [] + for p in parts: + matchref = cls.ref_pattern.match(p) + if matchref: + ref = matchref.group(1) + if not ref in ret: + ret.append(matchref.group(1)) + else: + ret.append(p) + return ret \ No newline at end of file