diff -r 000000000000 -r 2e8eeb919028 configurationengine/source/cone/public/utils.py --- /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