configurationengine/source/cone/public/utils.py
changeset 0 2e8eeb919028
child 3 e7e0ae78773e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/cone/public/utils.py	Thu Mar 11 17:04:37 2010 +0200
@@ -0,0 +1,621 @@
+#
+# 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: 
+#
+
+
+import os
+import re
+import StringIO
+import tokenize
+import inspect
+import traceback
+import logging
+import imghdr
+from token import ENDMARKER, NAME, NUMBER, STRING
+import api
+import mimetypes
+import exceptions
+
+import _etree_wrapper
+etree = _etree_wrapper.ElementTreeWrapper()
+
+class resourceref(object):
+    """
+    Class container for set of resource reference related functions
+    """
+    @classmethod
+    def filter_resources(cls, resources, regexp):
+        """
+        Filter out all resources that do not match the given regexp
+        @return a array of resources that match the given resource
+        """
+        test = re.compile(regexp, re.IGNORECASE)
+        return [r for r in resources if test.search(r)]
+    
+    @classmethod
+    def neg_filter_resources(cls, resources, regexp):
+        """
+        Filter out all resources that do match the given regexp
+        @return a array of resources that dont match the given resource
+        """
+        test = re.compile(regexp, re.IGNORECASE)
+        return [r for r in resources if not test.search(r)]
+
+    @classmethod
+    def insert_begin_slash(cls, ref):
+        if not ref.startswith('/'): 
+            return '/' + ref
+        return ref
+    
+    @classmethod
+    def remove_begin_slash(cls, ref):
+        if ref.startswith('/'): 
+            return ref.replace('/', '', 1)
+        return ref
+
+    @classmethod
+    def remove_end(self, path, str):
+        try:
+            (ret, sep, rest) = path.partition(str)
+            return ret
+        except ValueError:
+            return path
+
+    @classmethod
+    def add_end_slash(cls, ref):
+        if not ref.endswith('/'): 
+            return ref+'/'
+        return ref
+    
+    @classmethod
+    def remove_end_slash(cls, ref):
+        if ref.endswith('/'): 
+            return ref[:-1]
+        return ref
+    
+    @classmethod
+    def norm(cls, ref):
+        """
+        Normalize the reference to common cone form. 
+        1. Always with forward slashes 
+        2. no beginning slash
+        3. no end slash
+        @return: A normalized reference string
+        """
+        
+        # Do not modify emtpy string at all
+        if not ref == '':
+            normref = os.path.normpath(ref)
+            normref = normref.replace('\\','/').replace('"','').replace('//','/')
+        else:
+            normref = ref
+        return normref
+    
+    @classmethod
+    def replace_dir(cls, ref, frompart, topart):
+        """
+        Replace a part of directory beginning from ref.
+        @param ref: the resource reference
+        @param frompart: the part of directory name to be replaced
+        @param topart: the partial name which replaces the frompart
+        @return: a refenence with forward slashes
+        """
+        # Normalize all paths and replace the name with string replace
+        # 
+        normref = cls.norm(ref)
+        normfrom = cls.norm(frompart)
+        normto = cls.norm(topart)
+        # Add the end slash to from and to as it should be a dir (if not empty)
+        if normto != "": normto = cls.add_end_slash(normto)
+        if normfrom != "": normfrom = cls.add_end_slash(normfrom)
+        if normref != "": normref = cls.add_end_slash(normref)
+        retref = cls.norm(normref.replace(normfrom, normto, 1))
+        if retref  != "": retref = cls.remove_end_slash(retref)
+        return retref
+
+    @classmethod
+    def join_refs(cls, refs):
+        """
+        join a list of dotted references together with dots
+        1. ignore empty refs
+        2. no dot include begin dot
+        3. no dot include end dot
+        @param refs: a list of references
+        @return: A normalized dotted reference
+        """
+        # Create a copy of references without any empty strings
+        import posixpath
+        paramdict = {}
+        retref = posixpath.join(*refs)
+        #retref = "/".join([ref for ref in refs if ref != ''])
+        #subs = re.sub('/+', '/', retref)
+        return retref
+
+    @classmethod
+    def split_ref(cls, ref):
+        """
+        Replace a part of directory beginning from ref.
+        @param ref: the resource reference
+        @return: a list of path elems
+        """
+        return [r for r in ref.split('/') if r]
+    
+    @classmethod
+    def psplit_ref(cls, ref):
+        """
+        pop split that splits the last element of the array 
+        1. empty ref returns an empty list
+        @param ref: a resource references string (e.g. aaa/bbb/ccc.txt)
+        @return: A tuple of references (with given param ('aaa/bbb','ccc.txt')
+        """
+        refs = ref.rsplit('/', 1)
+        return ("".join(refs[0:-1]), refs[-1])
+
+    @classmethod
+    def remove_ext(cls, ref):
+        """
+        Remove file extension from ref 
+        1. remove file extension
+        @return: a reference. E.g. (foo/test.confml) => foo/test
+        """
+        filenameparts = cls.get_filename(ref).rsplit('.', 1)
+        path = cls.get_path(ref)
+        if len(filenameparts)==2 and filenameparts[0] != "":
+            return cls.join_refs([path, filenameparts[0]])
+        else:
+            return ref
+
+    @classmethod
+    def get_ext(cls, ref):
+        """
+        get file extension from ref 
+        1. get file extension
+        @return: a reference. E.g. (foo/test.confml) => confml
+        """
+        if len(ref.rsplit('.', 1)) == 2: 
+            return ref.rsplit('.', 1)[1]
+        else:
+            return ""
+
+    @classmethod
+    def get_filename(cls, ref):
+        """
+        get file name part from ref 
+        1. get file extension
+        @return: a reference. E.g. (foo/test.confml) => confml
+        """
+        return ref.rsplit('/', 1)[-1]
+
+    @classmethod
+    def get_path(cls, ref):
+        """
+        get file name part from ref 
+        1. get file extension
+        @return: a reference. E.g. (foo/test.confml) => confml
+        """
+        if len(ref.rsplit('/', 1)) == 2: 
+            return ref.rsplit('/', 1)[0]
+        else:
+            return ""
+
+    @classmethod
+    def to_dottedref(cls, ref):
+        """
+        Convert a resource ref to dotted ref 
+        1. remove file extension
+        2. convert path delims to dots
+        @return: a dotted reference. E.g. (foo/test.confml) => foo_test
+        """
+        newref = cls.remove_ext(ref).replace('/', '_').replace(' ', '_')
+        return dottedref.remove_begin_dot(newref)
+
+
+    @classmethod
+    def to_objref(cls, ref):
+        """
+        Convert a resource ref to dotted ref 
+        1. remove file extension
+        2. convert path delims to dots
+        3. using double underscores for directory separation
+        @return: a dotted reference. E.g. (foo/test.confml) => foo_test
+        """
+        ref = ref.replace('/', '__')
+        newref = ''
+        first_token = True
+        try:
+            for toknum, tokval, spos, epos, _ in tokenize.generate_tokens(StringIO.StringIO(unicode(ref)).readline):
+                if toknum == ENDMARKER:
+                    break
+                elif toknum == NAME:
+                    newref += str(tokval)
+                elif toknum == NUMBER:
+                    # Add a character before the number token if the first token is a number
+                    if first_token:
+                        newref += '_'
+                    # replace a possible dot in number .123
+                    newref += str(tokval.replace('.','_'))
+                elif toknum == STRING:
+                    newref += str(tokval.replace('"', ''))
+                else:
+                    newref += '_'
+                # After first round set the first token to false
+                first_token = False
+        except tokenize.TokenError:
+            pass
+        return newref
+        
+    @classmethod
+    def to_dref(cls, ref):
+        """
+        Convert a resource ref to dotted ref 
+        1. remove file extension
+        2. convert path delims to dots
+        @return: a dotted reference. E.g. (foo/test.confml) => foo.test
+        """
+        return dottedref.remove_begin_dot(cls.remove_ext(ref).replace('/','.'))
+
+    @classmethod
+    def to_hash(cls, ref):
+        """
+        Convert a resource ref to to hash 32 bit integer
+        @return: 
+        """
+        return "%s" % hex(hash(ref))
+
+class dottedref(object):
+    """
+    Class container for set of dotted reference related functions
+    """
+    @classmethod
+    def join_refs(cls, refs):
+        """
+        join a list of dotted references together with dots
+        1. ignore empty refs
+        2. no dot include begin dot
+        3. no dot include end dot
+        @param refs: a list of references
+        @return: A normalized dotted reference
+        """
+        # Create a dotted reference without any empty strings
+        return '.'.join([ref for ref in refs if ref.strip()])
+    
+    @classmethod
+    def split_ref(cls, ref):
+        """
+        split a dotted references string to a list of ref elements
+        1. empty ref returns an empty list
+        @param ref: a dotted references string (e.g. aaa.bbb.ccc)
+        @return: A list of references (with given param ['aaa','bbb','ccc']
+        """
+        # list of reference parts without any empty strings
+        return [r for r in ref.split('.') if r]
+    
+    @classmethod
+    def psplit_ref(cls, ref):
+        """
+        pop split that splits the last element of the array 
+        1. empty ref returns an empty list
+        @param ref: a dotted references string (e.g. aaa.bbb.ccc)
+        @return: A tuple of references (with given param ('aaa.bbb','ccc')
+        """
+        refs = ref.rsplit('.', 1)
+        return ("".join(refs[0:-1]), refs[-1])
+    
+    @classmethod
+    def remove_last(cls, ref):
+        """
+        removes the last element of the ref 
+        1. empty ref returns an empty list
+        @param ref: a dotted references string (e.g. aaa.bbb.ccc)
+        @return: A reference (with given param ('aaa.bbb')
+        """
+        return ref.rsplit('.', 1)[0]
+
+    @classmethod
+    def get_last(cls, ref):
+        """
+        returns the last element of the ref 
+        1. empty ref returns an empty string
+        @param ref: a dotted references string (e.g. aaa.bbb.ccc)
+        @return: A reference (with given param ('ccc')
+        """
+        return ref.rsplit('.', 1)[-1]
+
+    @classmethod
+    def get_name(cls, ref):
+        """
+        returns the last element of the ref 
+        1. empty ref returns an empty string
+        @param ref: a dotted references string (e.g. aaa.bbb.ccc)
+        @return: A reference (with given param ('ccc')
+        """
+        if re.match('^(.*)\[.*\]$', ref):
+            return re.match('^(.*)\[.*\]$', ref).group(1)
+        else:
+            return ref
+
+    @classmethod
+    def get_index(cls, ref):
+        """
+        returns the last element of the ref 
+        1. empty ref returns an empty string
+        @param ref: a dotted references string (e.g. aaa.bbb.ccc)
+        @return: A reference (with given param ('ccc')
+        """
+        if re.match('^.*\[(\d+)\]$', ref):
+            return int( re.match('^.*\[(\d+)\]$', ref).group(1) )
+        else:
+            return None
+
+    @classmethod
+    def remove_begin_dot(cls, ref):
+        """
+        removes all the dots from the begin of the ref 
+        @param ref: a dotted references string (e.g. .aaa.bbb.ccc)
+        @return: A reference (with given param ('aaa.bbb.ccc')
+        """
+        return ref.lstrip('.')
+
+    @classmethod
+    def ref2filter(cls, ref):
+        elems = []
+        for refelem in dottedref.split_ref(ref):
+            if refelem == "**":
+                elems.append(".*")
+            else:
+                elems.append(refelem.replace("*","[^\.]*"))
+        return "\\.".join(elems)+"$"
+    
+def extract_delimited_tokens(string, delimiters=('${', '}')):
+    """
+    Return a list of all tokens delimited by the given strings in the given string.
+    This function returns basically the first row of the result of
+    extract_delimited_token_tuples(), with duplicates removed.
+    
+    >>> dottedref.extract_refs("test1 ${my.ref1} test2 ${ my.ref1 } ${my.ref2}")
+    ['my.ref1', 'my.ref2']
+    """
+    ref_tuples = extract_delimited_token_tuples(string, delimiters)
+    return distinct_array([ref for ref, raw_ref in ref_tuples])
+
+def extract_delimited_token_tuples(string, delimiters=('${', '}')):
+    """
+    Extract a list of (token, raw_token) tuples from the given string.
+    'token' is the the token extracted from the string and trimmed (surrounding
+    whitespace removed), and raw_token is the unmodified match from the
+    string, which can be used for replacing.
+    
+    >>> dottedref.extract_ref_tuples("test1 ${my.ref1} test2 ${ my.ref1 } ${my.ref2}")
+    [('my.ref1', '${my.ref1}'), ('my.ref1', '${ my.ref1 }'), ('my.ref2', '${my.ref2}')]
+    """
+    pattern = '%s.*?%s' % (re.escape(delimiters[0]), re.escape(delimiters[1]))
+    matches = distinct_array(re.findall(pattern, string, re.DOTALL))
+    
+    result = []
+    for match in matches:
+        ref = match[len(delimiters[0]):-len(delimiters[1])].strip()
+        result.append((ref, match))
+    return result
+
+def expand_delimited_tokens(string, expander_func, delimiters=('${', '}')):
+    """
+    Expand all tokens in the given string using the given expander function.
+    
+    @param string: The string to expand.
+    @param expander_func: The function used for expanding. Should take two parameters:
+        1 - The token to expand.
+        2 - The index of the token in the string.
+    @param delimiters: Tuple specifying the delimiters for tokens.
+    @return: The expanded string.
+    """
+    # Collect a dictionary of token-entry pairs
+    class Entry(object):
+        pass
+    tokens = {}
+    for index, (token, raw_token) in enumerate(extract_delimited_token_tuples(string, delimiters)):
+        if token not in tokens:
+            entry = Entry()
+            entry.index = index
+            entry.raw_tokens = []
+            entry.value = unicode(expander_func(token, index))
+            tokens[token] = entry
+        else:
+            entry = tokens[token]
+        
+        entry.raw_tokens.append(raw_token)
+    
+    # Replace all tokens with the expanded values
+    result = string
+    for entry in tokens.itervalues():
+        for raw_token in entry.raw_tokens:
+            result = result.replace(raw_token, entry.value)
+    return result
+
+def expand_refs_by_default_view(string, default_view, delimiters=('${', '}'), default_value_for_missing=''):
+    """
+    Convenience function for expanding the refs in a string using setting values.
+    @param default_value_for_missing: The default value used if a setting for
+        a reference cannot be found.
+    @return: The expanded string.
+    """
+    def expand(ref, index):
+        try:
+            return default_view.get_feature(ref).get_original_value()
+        except exceptions.NotFound:
+            logging.getLogger('cone').error("Feature '%s' not found" % ref)
+            return default_value_for_missing
+    return expand_delimited_tokens(string, expand, delimiters)
+
+def distinct_array(arr):
+    newarray = []
+    for val in arr:
+        try:
+            # test to see whether the value already is in thearray
+            newarray.index(val)
+        except ValueError:
+            newarray.append(val)
+    return newarray
+
+
+def list_files(path):    
+    """
+    Get an array of files in a folder
+    """
+    retarray = []
+    # Walk through all files in the layer
+    path = os.path.abspath(path)
+    for root, dirs, files in os.walk(path):
+        for name in files:
+            entry = os.path.join(root, name)
+            entry = os.path.normpath(os.path.abspath(entry))
+            if os.path.isfile(entry):
+                retarray.append(entry)
+    return retarray
+
+def all_subclasses(classname):
+    """
+    @return: A list of all subclasses of classname
+    """
+    subclasses = classname.__subclasses__()
+    # Create copy of the subclasses list for the iteration, 
+    # so that added items are not recursed again
+    for subclass in classname.__subclasses__():
+        subclasses += all_subclasses(subclass)
+    return subclasses
+
+def pathmatch(pattern, refpath):
+    """
+    Check for matching pattern for a ref path
+    """
+    filter = dottedref.ref2filter(pattern)
+    return re.match(filter, refpath) != None
+
+def filter(obj, filters):
+    for filter in filters:
+        if not filter(obj):
+            return False
+    return True
+
+def get_list(elem):
+    if not isinstance(elem, list):
+        return [elem]
+    else:
+        return elem
+
+def add_list(elem, add):
+    retlist = get_list(elem)
+    retlist.append(add)
+    return retlist
+
+def prepend_list(elem, prepend):
+    retlist = get_list(elem)
+    retlist.insert(0, prepend)
+    return retlist
+
+def is_list(elem):
+    return isinstance(elem, list)
+
+def get_class(modelinstance, classinstance):
+    """
+    Get the actual model specific implementation class for a classinstance
+    """
+    for attr in dir(modelinstance):
+        modelclass = getattr(modelinstance, attr)
+        if inspect.isclass(modelclass): 
+            if issubclass(modelclass, classinstance):
+                return modelclass
+    return classinstance
+
+class DataMapRef(object):
+    """
+    Utility class for handling map attributes in data section
+    """
+    @classmethod
+    def get_feature_ref(cls, map):
+        index = map.find("@")
+        if index != -1:
+            parts = map.split("@")
+            return parts[0][:-1]
+        else:
+            return None
+        
+    @classmethod
+    def get_key_value(cls, map):
+        index = map.find("@")
+        if index != -1:
+            parts = map.split("@")
+            key = parts[1][:-1]
+            keys = key.split("=")
+            value = keys[1].strip()
+            return value[1:-1]
+        else:
+            return None
+        
+
+class xml(object):
+    """
+    Class container for set of XML-related helper functions.
+    """
+    
+    @classmethod
+    def split_tag_namespace(cls, xml_tag):
+        """
+        Split the given XML tag into a (namespace, tag) tuple.
+        
+        >>> ReaderBase._split_tag_namespace("test")
+        (None, 'test')
+        >>> ReaderBase._split_tag_namespace("{http://www.test.com/xml/1}test")
+        ('http://www.test.com/xml/1', 'test')
+        """
+        if xml_tag.startswith('{'):
+            parts = xml_tag[1:].split('}')
+            return (parts[0], parts[1])
+        else:
+            return (None, xml_tag)
+
+def log_exception(logger, msg, msg_level=logging.ERROR, traceback_level=logging.DEBUG):
+    """
+    Log an exception so that the given message and the exception's
+    traceback are logged separately with the given log levels.
+    
+    The purpose is to print minimal information to the user when running
+    the CLI (default level for STDOUT logging is WARNING), but the traceback
+    should still be available in the log file (which uses the level DEBUG
+    by default).
+    
+    Note that this function should be only used in an exception handler.
+    """
+    logger.log(msg_level, msg)
+    logger.log(traceback_level, traceback.format_exc())
+
+def make_content_info(resource, data):
+    """
+    Factory for ContentInfo
+    """
+    cnt_inf = None
+    
+    if resource != None:
+        guessed_type = mimetypes.guess_type(resource.get_path())
+        mimetype = None
+        mimesubtype = None
+        
+        if guessed_type != None:
+            mimetype, mimesubtype = guessed_type[0].split('/') 
+        
+        if mimetype == 'image' and mimesubtype == 'x-ms-bmp':
+            cnt_inf = api.BmpImageContentInfo(resource, data)
+        else:
+            cnt_inf = api.ContentInfo(mimetype, mimesubtype)
+    return cnt_inf