--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/configurationengine/source/plugins/common/ConeTemplatePlugin/templatemlplugin/templatemlplugin.py Thu Mar 11 17:04:37 2010 +0200
@@ -0,0 +1,562 @@
+#
+# 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:
+#
+'''
+Template plugin for ConE that handles templateml files. Utilizes Jinja template engine.
+'''
+
+import re
+import os
+import sys
+import logging
+import codecs
+import xml.parsers.expat
+from jinja2 import Environment, PackageLoader, FileSystemLoader, Template, DictLoader
+import traceback
+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
+
+try:
+ from cElementTree import ElementInclude
+except ImportError:
+ try:
+ from elementtree import ElementInclude
+ except ImportError:
+ try:
+ from xml.etree import cElementInclude as ElementInclude
+ except ImportError:
+ from xml.etree import ElementInclude
+
+import __init__
+
+from cone.public import exceptions,plugin,utils,api
+from cone.confml import persistentconfml
+
+ROOT_PATH = os.path.dirname(os.path.abspath(__file__))
+
+class TemplatemlImpl(plugin.ImplBase):
+
+ context = None
+
+ """
+ Implementation class of template plugin.
+ """
+
+ IMPL_TYPE_ID = "templateml"
+
+
+ def __init__(self,ref,configuration, reader=None):
+ """
+ Overloading the default constructor
+ """
+ plugin.ImplBase.__init__(self,ref,configuration)
+ self.logger = logging.getLogger('cone.templateml(%s)' % self.ref)
+ self.errors = False
+ self.reader = reader
+ if self.reader and self.reader.tags:
+ self.set_tags(self.reader.tags)
+
+ def get_context(self):
+ if TemplatemlImpl.context == None:
+ TemplatemlImpl.context = self.create_dict()
+
+ return TemplatemlImpl.context
+
+ def generate(self, context=None):
+ """
+ Generate the given implementation.
+ """
+
+ self.create_output()
+ return
+
+ def create_output(self, layers=None):
+ generator = Generator(self.reader.outputs, self.reader.filters, self.get_context(), self.configuration)
+ generator.generate(self.output, self.ref)
+ return
+
+ def get_refs(self):
+ refs = []
+ for output in self.reader.outputs:
+ template = output.template.template
+ refs.extend(self._extract_refs_from_template(template))
+ return refs
+
+ @classmethod
+ def _extract_refs_from_template(cls, template_text):
+ refs = []
+ pattern = re.compile(r'feat_tree\.((?:\.?\w+)*)', re.UNICODE)
+ for m in re.finditer(pattern, template_text):
+ ref = m.group(1)
+
+ # ref may now be e.g. 'MyFeature.MySetting._value', so
+ # remove the last part if it starts with an underscore
+ index = ref.rfind('.')
+ if index != -1 and index < len(ref) and ref[index + 1] == '_':
+ ref = ref[:index]
+
+ refs.append(ref)
+ return refs
+
+ def has_ref(self, refs):
+ """
+ @returns True if the implementation uses the given ref as input value.
+ Otherwise return False.
+ """
+
+ # Does not support template inheritance
+
+ if not isinstance(refs, list):
+ refs = [refs]
+
+ for output in self.reader.outputs:
+ if re.search("feat_list.*", output.template.template) != None:
+ return True
+
+ refs_in_templates = self.get_refs()
+
+ for ref in refs:
+ if ref in refs_in_templates:
+ return True
+ return False
+
+
+ def list_output_files(self):
+ """ Return a list of output files as an array. """
+ result = []
+ for output in self.reader.outputs:
+ result.append(os.path.normpath(os.path.join(self.output, output.path, output.filename)))
+ return result
+
+ def create_dict(self):
+ """
+ Creates dict from configuration that can be passed to template engine.
+ """
+
+ context_dict = {}
+
+ if self.configuration:
+ dview = self.configuration.get_default_view()
+ feat_list = []
+ feat_tree = {}
+
+ def add_feature(feature, feature_dict):
+ fea_dict = FeatureDictProxy(feature)
+ feat_list.append(fea_dict)
+ feature_dict[feature.ref] = fea_dict
+
+ # Recursively add sub-features
+ for sfeat in feature.list_features():
+ add_feature(feature.get_feature(sfeat), fea_dict)
+
+ for fea in dview.list_features():
+ add_feature(dview.get_feature(fea), feat_tree)
+
+ context_dict['feat_list'] = feat_list
+ context_dict['feat_tree'] = feat_tree
+ context_dict['configuration'] = self.configuration
+
+ return context_dict
+
+def _expand_refs(text, config):
+ if config is not None:
+ return utils.expand_refs_by_default_view(text, config.get_default_view())
+ else:
+ return text
+
+def _read_relative_file(configuration, relative_path, file_path):
+ """
+ Read data from a file relative to the given other file path.
+ """
+ # Get the actual path (relative to the current file)
+ base_path = os.path.dirname(file_path)
+ tempfile_path = os.path.normpath(os.path.join(base_path, relative_path)).replace('\\', '/')
+
+ # Read the file
+ resource = configuration.get_resource(tempfile_path)
+ try: return resource.read()
+ finally: resource.close()
+
+class TemplatemlImplReader(plugin.ReaderBase):
+ """
+ Parses a single templateml file
+ """
+ NAMESPACE = 'http://www.s60.com/xml/templateml/1'
+ FILE_EXTENSIONS = ['templateml']
+
+ def __init__(self, resource_ref=None, configuration=None):
+ self.desc = None
+ self.namespaces = [self.NAMESPACE]
+ self.outputs = None
+ self.filters = None
+ self.tags = None
+ self.resource_ref = resource_ref
+ self.configuration = configuration
+
+
+ @classmethod
+ def read_impl(cls, resource_ref, configuration, etree):
+ reader = TemplatemlImplReader(resource_ref, configuration)
+ reader.from_elementtree(etree)
+ return TemplatemlImpl(resource_ref, configuration, reader)
+
+ def fromstring(self, xml_string):
+ etree = ElementTree.fromstring(xml_string)
+ self.from_elementtree(etree)
+
+ def from_elementtree(self, etree):
+ ElementInclude.include(etree)
+ self.desc = self.parse_desc(etree)
+ self.outputs = self.parse_outputs(etree)
+ self.filters = self.parse_filters(etree)
+ self.tags = self.parse_tags(etree)
+
+ 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_filters(self, etree):
+ filters = []
+ filter_elems = etree.findall("{%s}filter" % self.namespaces[0])
+ for filter_elem in filter_elems:
+ if filter_elem != None:
+ filter = Filter()
+
+ if filter_elem.get('name') != None:
+ name = filter_elem.get('name')
+ if self.configuration != None:
+ name = utils.expand_refs_by_default_view(name, self.configuration.get_default_view())
+ filter.set_name(name)
+ if filter_elem.get('file') != None:
+ file = filter_elem.get('file')
+ if self.configuration != None:
+ file = utils.expand_refs_by_default_view(file, self.configuration.get_default_view())
+ filter.set_path(file)
+ if filter_elem.text != None:
+ filter.set_code(filter_elem.text)
+ if filter_elem.get('file') != None:
+ logging.getLogger('cone.templateml').warning("In filter element file attribute and text defined. Using filter found from file attribute.")
+ filters.append(filter)
+ return filters
+
+ 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_template(self, output_elem):
+ tempfile = TempFile()
+ template_elems = output_elem.findall("{%s}template" % self.namespaces[0])
+
+ for template_elem in template_elems:
+ if template_elem.text != None:
+ tempfile.set_template(template_elem.text)
+ else:
+ for selem in template_elem:
+ tempfile.set_template(selem.text)
+
+ if template_elem.get('file') != None:
+ file = template_elem.get('file')
+ if template_elem.text != None:
+ logging.getLogger('cone.templateml').warning("In template element file attribute and text defined. Using template found from file attribute.")
+ template_text = _read_relative_file(self.configuration, file, self.resource_ref)
+ tempfile.set_template(template_text)
+ return tempfile
+
+ def parse_outputs(self, etree):
+ outputs = []
+ output_elems = etree.findall("{%s}output" % self.namespaces[0])
+ for output_elem in output_elems:
+ if output_elem != None:
+ outputfile = OutputFile()
+ if output_elem.get('encoding') != None:
+ encoding = output_elem.get('encoding')
+ # Check the encoding
+ try:
+ codecs.lookup(encoding)
+ except LookupError:
+ raise exceptions.ParseError("Invalid output encoding: %s" % encoding)
+
+ if self.configuration != None:
+ encoding = utils.expand_refs_by_default_view(encoding, self.configuration.get_default_view())
+ outputfile.set_encoding(encoding)
+ if output_elem.get('file') != None:
+ file = output_elem.get('file')
+
+ if self.configuration != None:
+ file = utils.expand_refs_by_default_view(file, self.configuration.get_default_view())
+ outputfile.set_filename(file)
+ if output_elem.get('dir') != None:
+ dir = output_elem.get('dir')
+ if self.configuration != None:
+ dir = utils.expand_refs_by_default_view(dir, self.configuration.get_default_view())
+ outputfile.set_path(dir)
+ if output_elem.get('ref'):
+ # Fetch the output value from a configuration reference
+ fea = self.configuration.get_default_view().get_feature(output_elem.get('ref'))
+ outputfile.set_filename(fea.value)
+ if output_elem.get('bom'):
+ outputfile.bom = output_elem.get('bom').lower() in ('1', 'true', 't', 'yes', 'y')
+ outputfile.set_template(self.parse_template(output_elem))
+ outputfile.set_filters(self.parse_filters(output_elem))
+ outputs.append(outputfile)
+ return outputs
+
+class Generator(object):
+ """
+ Class that generates
+ """
+
+ def __init__(self, outputs, filters, context, configuration=None):
+ self.outputs = outputs
+ self.filters = filters
+ self.context = context
+ self.configuration = configuration
+
+ def generate(self, output_path, ref):
+ """
+ Generates output based on templates
+ """
+ if self.outputs != None:
+
+ for output in self.outputs:
+ try:
+ logging.getLogger('cone.templateml').debug(output)
+ out_path = os.path.abspath(os.path.join(output_path, output.path))
+ if out_path != '':
+ if not os.path.exists(out_path):
+ os.makedirs(out_path)
+
+ out_file = open(os.path.join(out_path, output.filename), 'wb')
+
+ if output.template.path:
+ output.template.template = _read_relative_file(self.configuration, output.template.path, ref)
+
+ dict_loader = DictLoader({'template': output.template.template})
+ env = Environment(loader=dict_loader)
+
+ # Common filters
+ for filter in self.filters:
+
+ if filter.path:
+ filter.code = _read_relative_file(self.configuration, filter.path, ref)
+
+ if not filter.code:
+ logging.getLogger('cone.templateml').warning("Skipping empty filter definition.")
+ else:
+ env.filters[str(filter.name)] = eval(filter.code)
+
+ # Output file specific filters
+ for filter in output.filters:
+ if filter.path:
+ filter.code = _read_relative_file(self.configuration, filter.path, ref)
+
+ if not filter.code:
+ logging.getLogger('cone.templateml').warning("Skipping empty filter definition.")
+ else:
+ env.filters[str(filter.name)] = eval(filter.code)
+
+ template = env.get_template('template')
+
+ file_string = template.render(self.context)
+ out_file.write(self._encode_data(file_string, output.encoding, output.bom))
+ out_file.close()
+
+ except Exception, e:
+ logging.getLogger('cone.templateml').error('Failed to generate template: %s %s\n%s' % (type(e), e, traceback.format_exc()) )
+ else:
+ logging.getLogger('cone.templateml').info('No (valid) templates found.')
+
+ def _encode_data(self, data, encoding, write_bom):
+ """
+ Encode the given data using the given encoding and BOM definition.
+ @param data: The data to encode.
+ @param encoding: The encoding to use.
+ @param write_bom: True or False to define whether the BOM should be written
+ for Unicode encodings, None for default.
+ """
+ data = data.encode(encoding)
+
+ # Check if we need to do special handling for BOM
+ if write_bom is not None:
+ BOM_MAPPING = {'utf-8' : codecs.BOM_UTF8,
+ 'utf-16' : codecs.BOM_UTF16,
+ 'utf-16-be' : codecs.BOM_UTF16_BE,
+ 'utf-16-le' : codecs.BOM_UTF16_LE}
+
+ # Use the name from a CodecInfo object to account for
+ # aliases (e.g. U8 and UTF-8 both map to utf-8)
+ codec_info = codecs.lookup(encoding)
+ if codec_info.name in BOM_MAPPING:
+ # Add or remove as necessary
+ BOM = BOM_MAPPING[codec_info.name]
+ if write_bom == True and not data.startswith(BOM):
+ data = BOM + data
+ elif write_bom == False and data.startswith(BOM):
+ data = data[len(BOM):]
+ return data
+
+
+class OutputFile(object):
+ def __init__(self):
+ self.filename = ''
+ self.path = ''
+ self.encoding = "utf-8"
+ self.template = TempFile()
+ self.filters = []
+ self.bom = None
+
+ def set_filename(self, filename):
+ self.filename = filename
+
+ def set_path(self, path):
+ self.path = path
+
+ def set_encoding(self, encoding):
+ self.encoding = encoding
+
+ def set_template(self, template):
+ self.template = template
+
+ def add_filter(self, filter):
+ self.filters.append(filters)
+
+ def set_filters(self, filters):
+ self.filters = filters
+
+ def __eq__(self, other):
+ if (self.template == other.template and self.encoding == other.encoding and self.path == other.path and self.filename == other.filename and self.filters == other.filters):
+ return True
+ return False
+
+ def __repr__(self):
+ return "OutputFile(filename=%r, path=%r, encoding=%r, template=%r, filters=%r" % (self.filename, self.path, self.encoding, self.template, self.filters)
+
+class TempFile(object):
+ def __init__(self):
+ self.template = ""
+ self.extensions = []
+ self.filters = []
+ self.path = ''
+
+ def set_path(self, path):
+ self.path = path
+
+ def set_template(self, template):
+ self.template = template
+
+ def add_extension(self, extension):
+ self.extensions.append(extension)
+
+ def add_filter(self, filter):
+ self.filters.append(filter)
+
+ def add_filter2(self, name, code):
+ self.filters.append(Filter(name, code))
+
+ def __eq__(self, other):
+ if self.template == other.template and self.filters == other.filters and self.extensions == other.extensions and self.path == other.path:
+ return True
+ return False
+
+class Filter(object):
+ def __init__(self, name, code):
+ self.name = name
+ self.code = code
+ self.path = None
+
+ def __init__(self):
+ self.name = None
+ self.code = None
+ self.path = None
+
+ def set_path(self, path):
+ self.path = path
+
+ def set_name(self, name):
+ self.name = name
+
+ def set_code(self, code):
+ self.code = code
+
+ def __eq__(self, other):
+ if self.name == other.name and self.code == other.code and self.path == other.path:
+ return True
+ return False
+
+class FeatureDictProxy(object):
+ """
+ Proxy class that behaves like a dictionary, but loads attributes from
+ the Feature object it proxies only when they are requested.
+ """
+ def __init__(self, feature):
+ self._feature = feature
+ self._children = {}
+
+ def _get_dict(self):
+ result = {
+ '_name' : self._feature.name,
+ '_namespace' : self._feature.namespace,
+ '_value' : self._feature.get_value(),
+ '_fqr' : self._feature.fqr,
+ '_type' : self._feature.type}
+ for ref, obj in self._children.iteritems():
+ result[ref] = obj
+ return result
+
+ def items(self):
+ return self._get_dict().items()
+
+ def iteritems(self):
+ return self._get_dict().iteritems()
+
+ def __getitem__(self, name):
+ if name == '_name': return self._feature.name
+ elif name == '_namespace': return self._feature.namespace
+ elif name == '_value': return self._feature.get_value()
+ elif name == '_fqr': return self._feature.fqr
+ elif name == '_type': return self._feature.type
+ else: return self._children[name]
+
+ def __setitem__(self, name, value):
+ self._children[name] = value
+
+ def __len__(self):
+ return len(self._get_dict())
+
+ def __eq__(self, other):
+ if not isinstance(other, dict):
+ return False
+ else:
+ return self._get_dict() == other
+
+ def __ne__(self, other):
+ return not (self == other)