configurationengine/source/plugins/common/ConeLegacyRulePlugin/legacyruleplugin/ruleml.py
changeset 3 e7e0ae78773e
child 4 0951727b8815
equal deleted inserted replaced
2:87cfa131b535 3:e7e0ae78773e
       
     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 Legacy RuleML plug-in to support RuleML 1 and 2 after rule engine changes
       
    18 in cone.public.rules. The new RuleML version is 3, and it uses the setting
       
    19 reference format ${MyFeature.MySetting} in the rules.
       
    20 
       
    21 The rule engine (rules.py) before the change is contained in this plug-in.
       
    22 
       
    23 NOTE THAT THIS PLUG-IN WILL NOT BE MAINTAINED, ALL NEW DEVELOPMENT SHOULD
       
    24 HAPPEN IN THE NEW RULE PLUG-IN. IF NEW FUNCTIONALITY IS REQUIRED IN AN
       
    25 EXISTING RULEML FILE, THE CHANGES SHOULD BE MADE TO THE NEW RULE PLUG-IN
       
    26 AND THE EXISTING RULEML FILE CONVERTED TO RULEML 3.
       
    27 '''
       
    28 
       
    29 
       
    30 import os
       
    31 import sys
       
    32 import logging
       
    33 import shutil
       
    34 import pkg_resources
       
    35 
       
    36 import __init__
       
    37 import re
       
    38 
       
    39 from legacyruleplugin import relations, rules
       
    40 from cone.public import exceptions,plugin,utils,api
       
    41 
       
    42 class RuleImpl(plugin.ImplBase):
       
    43     """
       
    44     MakeImpl plugin finds feature references that are configured in a .ruleml file
       
    45     and generate a rule from them
       
    46     """
       
    47     IMPL_TYPE_ID = 'ruleml'
       
    48     DEFAULT_INVOCATION_PHASE = 'pre'
       
    49     
       
    50     def __init__(self, ref, configuration, relation_container):
       
    51         """
       
    52         Overloading the default constructor
       
    53         """
       
    54         plugin.ImplBase.__init__(self,ref,configuration)
       
    55         self.relation_container = relation_container
       
    56 
       
    57     def list_output_files(self):
       
    58         """
       
    59         Return a list of output files as an array. 
       
    60         """
       
    61         return []
       
    62     
       
    63     def generate(self, context=None):
       
    64         relation_container = self.get_relation_container()
       
    65         relation_container.context = context
       
    66         return relation_container.execute(context)
       
    67     
       
    68     def has_tag(self, tags, policy=None):
       
    69         # RuleML should always be executed, regardless of the tags
       
    70         return True
       
    71     
       
    72     def get_relation_container(self):
       
    73         return self.relation_container
       
    74     
       
    75     def get_refs(self):
       
    76         """
       
    77         Return a list of all ConfML setting references that affect this
       
    78         implementation. May also return None if references are not relevant
       
    79         for the implementation.
       
    80         """
       
    81         refs = []
       
    82         relations = self.get_relations()
       
    83         for relation in relations:
       
    84             # get refs from relation return a tuple (left side refs, right side refs)
       
    85             # only the left side refs are the "input" refs  
       
    86             refs += relation.get_refs()
       
    87         # If the rules do not have any references return None to disable filter by refs 
       
    88         if refs == []: 
       
    89             refs = None
       
    90         return refs
       
    91     
       
    92     def get_relations(self):
       
    93         return self.relation_container.get_relations()
       
    94 
       
    95 class RuleBuiltinsModule(object):
       
    96     pass
       
    97 
       
    98 class RulemlRelationContainer(plugin.RelationContainer):
       
    99     """
       
   100     Relation container for RuleML rules.
       
   101     
       
   102     Basically this is a wrapper for rules.RelationContainer that adapts
       
   103     it to the interface of plugin.RelationContainer.
       
   104     """
       
   105     def __init__(self, configuration, source, rule_list, eval_globals):
       
   106         plugin.RelationContainer.__init__(self, configuration, source=source)
       
   107         self.configuration = configuration
       
   108         self.relation_container = rules.RelationContainerImpl()
       
   109         self.eval_globals = eval_globals
       
   110         self.context = None
       
   111         for rule in rule_list:
       
   112             self.relation_container.add_relation(rule)
       
   113     
       
   114     def execute(self, context=None):
       
   115         results = []
       
   116         
       
   117         # Create the autoconfig if not done already
       
   118         autoconfig = plugin.get_autoconfig(self.configuration)
       
   119         
       
   120         # Register relations etc. to the rule engine.
       
   121         # Due to unit test issues the relations are not registered
       
   122         # in the relations module, but only for the duration of
       
   123         # rule parsing and execution
       
   124         relations.register()
       
   125         try:
       
   126             # Using the configuration to pass the eval globals dict to the
       
   127             # eval expression. The configuration only contains the globals
       
   128             # dict for the duration of the rule execution, so hopefully this
       
   129             # shouldn't mess anything up
       
   130             self._set_builtin_eval_globals()
       
   131             context._eval_expression_globals_dict = self.eval_globals
       
   132             for i, rel in enumerate(self.relation_container):
       
   133                 index = i + 1
       
   134                 
       
   135                 # Execute
       
   136                 self._execute_relation_and_log_error(rel, self.source, index, context)
       
   137                 
       
   138                 # Collect execution result if supported
       
   139                 if hasattr(rel, 'get_execution_result'):
       
   140                     result = rel.get_execution_result()
       
   141                     if isinstance(result, plugin.RelationExecutionResult):
       
   142                         result.source = self.source
       
   143                         result.index = index
       
   144                         results.append(result)
       
   145             
       
   146             del context._eval_expression_globals_dict
       
   147             
       
   148             if self.relation_container.has_errors():
       
   149                 for error in self.relation_container.get_errors():
       
   150                     logging.getLogger('cone.ruleml_relation_container(%s)' % self.source).error(error)
       
   151             
       
   152             if self.context:
       
   153                 self.context.results += results
       
   154                 self.context.add_changed_refs(autoconfig.list_leaf_datas())
       
   155             return results
       
   156         finally:
       
   157             relations.unregister()
       
   158     
       
   159     def get_relation_count(self):
       
   160         return len(self.relation_container)
       
   161     
       
   162     def get_relations(self):
       
   163         return list(self.relation_container)
       
   164     
       
   165     def _set_builtin_eval_globals(self):
       
   166         """
       
   167         Add built-in attributes into the eval globals dictionary.
       
   168         """
       
   169                 
       
   170         builtins = RuleBuiltinsModule()
       
   171         builtins.configuration = self.configuration
       
   172         
       
   173         self.eval_globals['ruleml'] = builtins
       
   174 
       
   175 class RuleImplReaderBase(plugin.ReaderBase):
       
   176     NAMESPACE = None # Used as a base class, so should have no namespace
       
   177     FILE_EXTENSIONS = ['ruleml']
       
   178     
       
   179     def __init__(self, resource_ref, configuration):
       
   180         self.resource_ref = resource_ref
       
   181         self.configuration = configuration
       
   182         
       
   183     @classmethod
       
   184     def read_impl(cls, resource_ref, configuration, etree):
       
   185         reader = cls(resource_ref, configuration)
       
   186         
       
   187         # Register relations etc. to the rule engine.
       
   188         # Due to unit test issues the relations are not registered
       
   189         # in the relations module, but only for the duration of
       
   190         # rule parsing and execution
       
   191         relations.register()
       
   192         try:
       
   193             rules = reader.parse_rules(resource_ref, etree)
       
   194             eval_globals = reader.parse_eval_globals(etree)
       
   195             lineno = utils.etree.get_lineno(etree)
       
   196             
       
   197             # Create an ImplContainer to hold each rule as its own
       
   198             # RuleML implementation
       
   199             main_impl = plugin.ImplContainer(resource_ref, configuration)
       
   200             main_impl.lineno = lineno
       
   201             
       
   202             for rule in rules:
       
   203                 relation_container = RulemlRelationContainer(
       
   204                     configuration   = configuration,
       
   205                     source          = "%s:%d" % (resource_ref, rule.lineno),
       
   206                     rule_list       = [rule],
       
   207                     eval_globals    = eval_globals)
       
   208             
       
   209                 impl = RuleImpl(resource_ref, configuration, relation_container)
       
   210                 impl.lineno = rule.lineno
       
   211                 rule.implml = impl
       
   212                 
       
   213                 main_impl.append(impl)
       
   214         finally:
       
   215             relations.unregister()
       
   216         
       
   217         return main_impl
       
   218         
       
   219 class RuleImplReader1(RuleImplReaderBase):
       
   220     NAMESPACE = 'http://www.s60.com/xml/ruleml/1'
       
   221     NAMESPACE_ID = 'ruleml1'
       
   222     ROOT_ELEMENT_NAME = 'ruleml'
       
   223     
       
   224     def __init__(self, resource_ref, configuration):
       
   225         RuleImplReaderBase.__init__(self, resource_ref, configuration)
       
   226     
       
   227     @classmethod
       
   228     def get_schema_data(cls):
       
   229         return pkg_resources.resource_string('legacyruleplugin', 'xsd/ruleml.xsd')
       
   230     
       
   231     def parse_rules(self, ref, etree):
       
   232         rules = []
       
   233         for elem in etree.getiterator("{%s}rule" % self.NAMESPACE):
       
   234             lineno = utils.etree.get_lineno(elem)
       
   235             for rule in relations.RelationFactory.get_relations(self.configuration, elem.text):
       
   236                 rule.ref = ref
       
   237                 rule.lineno = lineno
       
   238                 rules.append(rule)
       
   239         return rules
       
   240     
       
   241     def parse_eval_globals(self, etree):
       
   242         return {}
       
   243 
       
   244 class RuleImplReader2(RuleImplReaderBase):
       
   245     NAMESPACE = 'http://www.s60.com/xml/ruleml/2'
       
   246     NAMESPACE_ID = 'ruleml2'
       
   247     ROOT_ELEMENT_NAME = 'ruleml'
       
   248     
       
   249     def __init__(self, resource_ref, configuration):
       
   250         RuleImplReaderBase.__init__(self, resource_ref, configuration)
       
   251     
       
   252     @classmethod
       
   253     def get_schema_data(cls):
       
   254         return pkg_resources.resource_string('legacyruleplugin', 'xsd/ruleml2.xsd')
       
   255     
       
   256     def parse_rules(self, ref, etree):
       
   257         rules = []
       
   258         for elem in etree.getiterator("{%s}rule" % self.NAMESPACE):
       
   259             lineno = utils.etree.get_lineno(elem)
       
   260             for rule in relations.RelationFactory.get_relations(self.configuration, self._replace_eval_blocks(elem.text)):
       
   261                 rule.ref = ref
       
   262                 rule.lineno = lineno
       
   263                 rules.append(rule)
       
   264         return rules
       
   265     
       
   266     def parse_eval_globals(self, etree):
       
   267         eval_globals = {}
       
   268         for elem in etree.getiterator("{%s}eval_globals" % self.NAMESPACE):
       
   269             text = ""
       
   270             if elem.get('file') != None:
       
   271                 self._read_eval_globals_from_file(elem.get('file'), eval_globals)
       
   272             else:
       
   273                 try: 
       
   274                     # Strip surrounding whitespace, otherwise there might be Python
       
   275                     # indentation errors
       
   276                     text = elem.text.strip()
       
   277                     exec(text, eval_globals)
       
   278                 except Exception, e:
       
   279                     logging.getLogger('cone.ruleml(%s)' % self.resource_ref).warning('Failed to evaluate eval_globals block, exception: %s' % (e))
       
   280         return eval_globals
       
   281     
       
   282     def _read_eval_globals_from_file(self, relative_path, eval_globals):
       
   283         # Get the actual path (relative to the current implementation file)
       
   284         base_path = os.path.dirname(self.resource_ref)
       
   285         pyfile_path = os.path.normpath(os.path.join(base_path, relative_path)).replace('\\', '/')
       
   286         # Read the data and execute
       
   287         try:
       
   288             resource = None
       
   289             resource = self.configuration.get_resource(pyfile_path)
       
   290             text = resource.read()
       
   291             exec(text.replace('\r', ''), eval_globals)
       
   292         except Exception, e:
       
   293             logging.getLogger('cone.ruleml(%s)' % self.resource_ref).warning('Cannot import eval file: %s. Exception: %s' % (pyfile_path, e))
       
   294         finally:
       
   295             if resource is not None: resource.close()
       
   296         
       
   297     
       
   298     @classmethod
       
   299     def _replace_eval_blocks(cls, code):
       
   300         return utils.expand_delimited_tokens(
       
   301             string          = code,
       
   302             expander_func   = lambda ref, index: '__eval__ %r' % ref,
       
   303             delimiters      =('{%', '%}'))