configurationengine/source/plugins/common/ConeContentPlugin/contentplugin/contentmlparser.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
equal deleted inserted replaced
-1:000000000000 0:2e8eeb919028
       
     1 #
       
     2 # Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     3 # All rights reserved.
       
     4 # This component and the accompanying materials are made available
       
     5 # under the terms of "Eclipse Public License v1.0"
       
     6 # which accompanies this distribution, and is available
       
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 #
       
     9 # Initial Contributors:
       
    10 # Nokia Corporation - initial contribution.
       
    11 #
       
    12 # Contributors:
       
    13 #
       
    14 # Description:
       
    15 #
       
    16 ## 
       
    17 # @author Teemu Rytkonen
       
    18 '''
       
    19 A plugin implementation for content selection from ConfigurationLayers.
       
    20 '''
       
    21 
       
    22 
       
    23 import re
       
    24 import os
       
    25 import sys
       
    26 import logging
       
    27 import shutil
       
    28 import copy
       
    29 import xml.parsers.expat
       
    30 
       
    31 try:
       
    32     from cElementTree import ElementTree
       
    33 except ImportError:
       
    34     try:    
       
    35         from elementtree import ElementTree
       
    36     except ImportError:
       
    37         try:
       
    38             from xml.etree import cElementTree as ElementTree
       
    39         except ImportError:
       
    40             from xml.etree import ElementTree
       
    41             
       
    42 import __init__
       
    43 
       
    44 from cone.public import exceptions,plugin,utils,api
       
    45 
       
    46 class ContentOutput(object):
       
    47     def __init__(self, **kwargs):
       
    48         self._dir = kwargs.get('dir',None)
       
    49         self._file = kwargs.get('file',None)
       
    50         self.flatten = kwargs.get('flatten',False)
       
    51         self.inputs = kwargs.get('inputs', [])
       
    52         self.configuration = None
       
    53 
       
    54     def set_configuration(self, configuration):
       
    55         self.configuration = configuration
       
    56         for input in self.inputs:
       
    57             input.configuration = self.configuration
       
    58 
       
    59     def get_configuration(self, configuration):
       
    60         self.configuration = configuration
       
    61 
       
    62     def path_convert(self, path):
       
    63         (drive, tail) = os.path.splitdrive(path)
       
    64         return tail.lstrip('\\/')
       
    65 
       
    66     def get_dir(self):
       
    67         if self.configuration and ConfmlRefs.is_confml_ref(self._dir):
       
    68             parts = self._dir.split(ConfmlRefs.ref_separator)
       
    69             for (index, part) in enumerate(parts):
       
    70                 if ConfmlRefs.is_confml_ref(part):
       
    71                     ref = ConfmlRefs.get_confml_ref(part)
       
    72                     parts[index] = self.configuration.get_default_view().get_feature(ref).value
       
    73                     parts[index] = self.path_convert(parts[index])
       
    74                 else:
       
    75                     parts[index] = part
       
    76             return (os.sep).join(parts)
       
    77         else:
       
    78             return self.path_convert(self._dir)
       
    79 
       
    80     def set_dir(self, dir):
       
    81         self._dir = dir
       
    82 
       
    83     def get_file(self):
       
    84         if self.configuration and self._file != None and ConfmlRefs.is_confml_ref(self._file):
       
    85             parts = self._file.split(ConfmlRefs.ref_separator)
       
    86             for (index, part) in enumerate(parts):
       
    87                 if ConfmlRefs.is_confml_ref(part):
       
    88                     ref = ConfmlRefs.get_confml_ref(part)
       
    89                     parts[index] = self.configuration.get_default_view().get_feature(ref).value
       
    90                     parts[index] = self.path_convert(parts[index])
       
    91                 else:
       
    92                     parts[index] = part
       
    93             return (os.sep).join(parts)
       
    94         else:
       
    95             return self._file
       
    96 
       
    97     def set_file(self, file):
       
    98         self._file = file
       
    99 
       
   100     dir = property(get_dir, set_dir)
       
   101     file = property(get_file, set_file)
       
   102     
       
   103     def get_refs(self):
       
   104         refs = []
       
   105         for input in self.inputs:
       
   106             refs.extend(input.get_refs())
       
   107         return refs
       
   108 
       
   109 
       
   110 class ContentInput(object):
       
   111     def __init__(self, **kwargs):
       
   112         self._dir = kwargs.get('dir',None)
       
   113         self._file = kwargs.get('file',None)
       
   114         self._include = kwargs.get('include', {})
       
   115         self._exclude = kwargs.get('exclude', {})
       
   116         self.configuration = None
       
   117 
       
   118     @property 
       
   119     def dir(self):
       
   120         
       
   121         if self.configuration and ConfmlRefs.is_confml_ref(self._dir):
       
   122             cref = ConfmlRefs.get_confml_ref(self._dir)
       
   123             return self.configuration.get_default_view().get_feature(cref).value
       
   124         else:
       
   125             return self._dir
       
   126 
       
   127     @property 
       
   128     def file(self):
       
   129         
       
   130         if self._file and self.configuration and ConfmlRefs.is_confml_ref(self._file):
       
   131             cref = ConfmlRefs.get_confml_ref(self._file)
       
   132             return self.configuration.get_default_view().get_feature(cref).value
       
   133         else:
       
   134             return self._file
       
   135 
       
   136 
       
   137     def _dereference_dict(self, data):
       
   138         if self.configuration:
       
   139             # Make a deep copy of the data, or otherwise get_refs() will
       
   140             # return the correct refs only on the first call, as the
       
   141             # references are replaced here
       
   142             data = copy.deepcopy(data)
       
   143             
       
   144             dview = self.configuration.get_default_view()
       
   145             for key in data:
       
   146                 key_list = data.get(key)
       
   147                 for (index,elem) in enumerate(key_list):
       
   148                     if ConfmlRefs.is_confml_ref(elem):
       
   149                         cref = ConfmlRefs.get_confml_ref(elem)
       
   150                         try:
       
   151                             # change None value to empty string
       
   152                             cvalue = dview.get_feature(cref).value or ''
       
   153                             if utils.is_list(cvalue):
       
   154                                 cvalue = ", ".join(cvalue)
       
   155                             key_list[index] = cvalue
       
   156                         except exceptions.NotFound:
       
   157                             logging.getLogger('cone.content').error("Feature ref '%s' in include key '%s' not found." % (cref,key))
       
   158         return data
       
   159 
       
   160     @property
       
   161     def include(self):
       
   162         return self._dereference_dict(self._include)
       
   163 
       
   164     @property
       
   165     def exclude(self):
       
   166         return self._dereference_dict(self._exclude)
       
   167 
       
   168     def get_filelist(self):
       
   169         filelist = []
       
   170         if self.file:
       
   171             filelist.append(self.file)
       
   172         for elem in self.include.get('files',[]):
       
   173             elem = elem.lower().split(',')
       
   174             filelist += [selem.strip() for selem in elem]
       
   175         return filelist
       
   176 
       
   177     def get_include_pattern(self):
       
   178         return self.include.get('pattern',[''])[0]
       
   179 
       
   180     def get_exclude_pattern(self):
       
   181         return self.exclude.get('pattern',[''])[0]
       
   182     
       
   183     def get_refs(self):
       
   184         refs = []
       
   185         if self._dir is not None:
       
   186             refs.extend(utils.extract_delimited_tokens(self._dir))
       
   187         if self._file is not None:
       
   188             refs.extend(utils.extract_delimited_tokens(self._file))
       
   189         for value_list in self._include.itervalues():
       
   190             for value in value_list:
       
   191                 refs.extend(utils.extract_delimited_tokens(value))
       
   192         for value_list in self._exclude.itervalues():
       
   193             for value in value_list:
       
   194                 refs.extend(utils.extract_delimited_tokens(value))
       
   195         return refs
       
   196 
       
   197 
       
   198 class ExternalContentInput(ContentInput):
       
   199     def __init__(self, **kwargs):
       
   200         super(ExternalContentInput,self).__init__(**kwargs)        
       
   201 
       
   202 class ContentParserBase(object):
       
   203     """
       
   204     Parses a single content implml file
       
   205     """ 
       
   206     NAMESPACES = ['http://www.s60.com/xml/content/1']
       
   207     INCLUDE_ATTR = ['pattern']
       
   208     EXCLUDE_ATTR = ['pattern']
       
   209     def __init__(self):
       
   210         self.namespaces = self.NAMESPACES
       
   211 
       
   212     def parse_phase(self,etree):
       
   213         phase = ""
       
   214         phase = etree.get('phase')
       
   215         return phase
       
   216 
       
   217     def parse_desc(self,etree):
       
   218         desc = ""
       
   219         desc_elem = etree.find("{%s}desc" % self.namespaces[0])
       
   220         if desc_elem != None:
       
   221             desc = desc_elem.text
       
   222         return desc
       
   223 
       
   224     def parse_tags(self,etree):
       
   225         tags = {}
       
   226         for tag in etree.getiterator("{%s}tag" % self.namespaces[0]):
       
   227             tagname = tag.get('name','')
       
   228             tagvalue = tag.get('value')
       
   229             values = tags.get(tagname,[])
       
   230             values.append(tagvalue)
       
   231             tags[tagname] = values
       
   232         return tags
       
   233 
       
   234     def parse_input_include(self,etree):
       
   235         include_elem = etree.getiterator("{%s}include" % self.namespaces[0])
       
   236         include = {}
       
   237         for f in include_elem:
       
   238             for key in f.keys():
       
   239                 # Add the attribute if it is found to include dict
       
   240                 include[key] = []
       
   241                 include[key].append(f.get(key))
       
   242         return include
       
   243 
       
   244     def parse_input_exclude(self,etree):
       
   245         elem = etree.getiterator("{%s}exclude" % self.namespaces[0])
       
   246         exclude = {}
       
   247         for f in elem:
       
   248             for key in f.keys():
       
   249                 # Add the attribute if it is found
       
   250                 exclude[key] = []
       
   251                 exclude[key].append(f.get(key))
       
   252         return exclude
       
   253 
       
   254 class Content1Parser(ContentParserBase):
       
   255     """
       
   256     Parses a single content implml file
       
   257     """ 
       
   258     NAMESPACES = ['http://www.s60.com/xml/content/1']
       
   259     def __init__(self):
       
   260         super(ContentParserBase,self).__init__()
       
   261         self.namespaces = self.NAMESPACES
       
   262 
       
   263     def parse_input(self,etree):
       
   264         input_elem = etree.find("{%s}input" % self.namespaces[0])
       
   265         input_dir = ""
       
   266         input_file = ""
       
   267         if input_elem != None:
       
   268             if input_elem.get('dir'):
       
   269                 input_dir = input_elem.get('dir')
       
   270             if input_elem.get('file'):
       
   271                 input_dir = input_elem.get('file')
       
   272             includes = self.parse_input_include(etree)
       
   273             excludes = self.parse_input_exclude(etree)
       
   274             return ContentInput(dir=input_dir, include=includes, exclude=excludes, file=input_file)
       
   275         return None
       
   276 
       
   277     def parse_outputs(self,etree):
       
   278         output_elem = etree.find("{%s}output" % self.namespaces[0])
       
   279         output_dir = ""        
       
   280         output_flatten = False
       
   281         inputs = []
       
   282         if output_elem != None:
       
   283             output_dir = output_elem.get('dir','')
       
   284             output_flatten = output_elem.get('flatten','') == "true"
       
   285         input = self.parse_input(etree)
       
   286         if input:
       
   287             inputs.append(input)
       
   288         return [ContentOutput(dir=output_dir, flatten=output_flatten, inputs=inputs)]
       
   289 
       
   290 class Content2Parser(ContentParserBase):
       
   291     """
       
   292     Parses a single content implml file
       
   293     """ 
       
   294     NAMESPACES = ['http://www.s60.com/xml/content/2']
       
   295     def __init__(self):
       
   296         super(ContentParserBase,self).__init__()
       
   297         self.namespaces = self.NAMESPACES
       
   298             
       
   299 
       
   300     def parse_input(self,input_elem):
       
   301         input = None
       
   302         input_dir = ''
       
   303         input_file = ''
       
   304         if input_elem != None:
       
   305             if input_elem.get('dir'):
       
   306                 input_dir = input_elem.get('dir')
       
   307             if input_elem.get('file'):
       
   308                 input_file= input_elem.get('file')
       
   309             includes = self.parse_input_include(input_elem)
       
   310             excludes = self.parse_input_exclude(input_elem)
       
   311             input = ContentInput(dir=input_dir, include=includes, exclude=excludes, file=input_file)
       
   312         return input
       
   313     
       
   314     def parse_external_input(self,input_elem):
       
   315         input = None
       
   316         input_dir = ''
       
   317         if input_elem != None:
       
   318             if input_elem.get('dir'):
       
   319                 input_dir = input_elem.get('dir')
       
   320             includes = self.parse_input_include(input_elem)
       
   321             excludes = self.parse_input_exclude(input_elem)
       
   322             input = ExternalContentInput(dir=input_dir, include=includes, exclude=excludes)
       
   323         return input
       
   324 
       
   325     def parse_outputs(self,etree):
       
   326         outputs = []
       
   327         for output_elem in etree.getiterator("{%s}output" % self.namespaces[0]):
       
   328             inputs = []
       
   329             output_dir = output_elem.get('dir','')
       
   330             output_file = output_elem.get('file','')
       
   331             output_flatten = output_elem.get('flatten','') == "true"
       
   332             for input_elem in output_elem.getiterator("{%s}input" % self.namespaces[0]):
       
   333                 inputs.append(self.parse_input(input_elem))
       
   334             for input_elem in output_elem.getiterator("{%s}externalinput" % self.namespaces[0]):
       
   335                 inputs.append(self.parse_external_input(input_elem))
       
   336             outputs.append(ContentOutput(dir=output_dir, flatten=output_flatten, inputs=inputs, file=output_file))
       
   337         return outputs
       
   338 
       
   339 
       
   340 class ContentImplReader(object):
       
   341     """
       
   342     Parses a single content implml file
       
   343     """ 
       
   344     PARSERS = {'http://www.s60.com/xml/content/1' : Content1Parser,
       
   345               'http://www.s60.com/xml/content/2' : Content2Parser}
       
   346     def __init__(self):
       
   347         self.desc = None
       
   348         self.outputs = None
       
   349         self.phase = None
       
   350 
       
   351     def fromstring(self, xml_as_string):
       
   352         etree = ElementTree.fromstring(xml_as_string)
       
   353         # Loop through parsers and try to find a match
       
   354         (namespace,elemname) = get_elemname(etree.tag)
       
   355         pclass = self.PARSERS.get(namespace, None)
       
   356         self.parser = pclass()
       
   357         self.desc = self.parser.parse_desc(etree)
       
   358         self.outputs = self.parser.parse_outputs(etree)
       
   359         self.phase = self.parser.parse_phase(etree)
       
   360         self.tags = self.parser.parse_tags(etree)
       
   361         return
       
   362 
       
   363 namespace_pattern = re.compile("{(.*)}(.*)")
       
   364 nonamespace_pattern = re.compile("(.*)")
       
   365 
       
   366 def get_elemname(tag):
       
   367     
       
   368     ns = namespace_pattern.match(tag)
       
   369     nn = nonamespace_pattern.match(tag)
       
   370     if ns:
       
   371         namespace = ns.group(1)
       
   372         elemname = ns.group(2)
       
   373         return (namespace,elemname)
       
   374     elif nn:
       
   375         namespace = ""
       
   376         elemname = nn.group(1)
       
   377         return (namespace,elemname)
       
   378     else:
       
   379         raise exceptions.ParseError("Could not parse tag %s" % tag)
       
   380 
       
   381 class ConfmlRefs(object):
       
   382     
       
   383     ref_pattern = re.compile('^\$\{(.*)\}$')
       
   384     cref_pattern = re.compile('.+\..+')
       
   385     ref_separator = '/'
       
   386        
       
   387     @classmethod
       
   388     def is_ref_like(cls, variableref):
       
   389         """
       
   390         
       
   391         Returns true if the given variable represents a ref
       
   392         """
       
   393         return cls.cref_pattern.match(variableref) != None
       
   394     
       
   395     @classmethod
       
   396     def is_confml_ref(cls, variableref):
       
   397         """
       
   398         
       
   399         Returns true if the given variable ref is a confml reference
       
   400         """
       
   401         
       
   402         pos = variableref.find(cls.ref_separator)
       
   403         if pos == -1:
       
   404             return cls.ref_pattern.match(variableref) != None
       
   405         else:
       
   406             return cls.is_confml_refs(variableref)
       
   407                 
       
   408     @classmethod
       
   409     def is_confml_refs(cls, variableref):
       
   410         """
       
   411         
       
   412         Returns true if the given variable ref is a confml reference
       
   413         """
       
   414         ret = False
       
   415         parts = variableref.split(cls.ref_separator)
       
   416         for p in parts:
       
   417             if cls.is_confml_ref(p) == True:
       
   418                 ret = True
       
   419         return ret
       
   420                 
       
   421 
       
   422     @classmethod
       
   423     def get_confml_ref(cls, variableref):
       
   424         """
       
   425         
       
   426         Returns true if the given variable ref is a confml reference
       
   427         """
       
   428         pos = variableref.find(cls.ref_separator)
       
   429         if pos == -1:
       
   430             matchref = cls.ref_pattern.match(variableref)
       
   431             if matchref:
       
   432                 return matchref.group(1)
       
   433         else:
       
   434             return cls.get_confml_refs(variableref)
       
   435 
       
   436     @classmethod
       
   437     def get_confml_refs(cls, variableref):
       
   438         """
       
   439         
       
   440         Returns an array of confml refs based on variableref
       
   441         """
       
   442         parts = variableref.split(cls.ref_separator)
       
   443         ret = []
       
   444         for p in parts:
       
   445             matchref = cls.ref_pattern.match(p)
       
   446             if matchref:
       
   447                 ref = matchref.group(1)
       
   448                 if not ref in ret:
       
   449                     ret.append(matchref.group(1))
       
   450             else:
       
   451 				ret.append(p)
       
   452         return ret