configurationengine/source/plugins/common/ConeLegacyRulePlugin/legacyruleplugin/relations.py
changeset 3 e7e0ae78773e
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 '''
       
    18 implementation for ruleml relations.
       
    19 '''
       
    20 import os
       
    21 import StringIO
       
    22 import logging
       
    23 import operator as ops
       
    24 import re
       
    25 import sys, traceback
       
    26 
       
    27 log = logging.getLogger('cone.ruleplugin.relations')
       
    28 
       
    29 from legacyruleplugin import rules
       
    30 from cone.public import api, utils, exceptions, plugin
       
    31 
       
    32 class RelationFactory(api.FactoryBase):
       
    33     @ classmethod
       
    34     def get_relation_by_name(cls, relation_name):
       
    35         """
       
    36         Get the class name by file extension.
       
    37         """
       
    38         try:
       
    39             return rules.RELATIONS.get(relation_name)
       
    40         except KeyError:
       
    41             raise exceptions.NotSupportedException("No Relation class found for name %s" % relation_name)
       
    42 
       
    43     @ classmethod
       
    44     def get_relations(cls, configuration, relation):
       
    45         try:
       
    46             relations = []
       
    47             (left_expression,relation_name,right_expression) = parse_rule(relation)
       
    48             relation = cls.get_relation_by_name(relation_name)(configuration, left_expression, right_expression)
       
    49             relations.append(relation)
       
    50             propagated_relations = cls.get_relations(configuration, right_expression)
       
    51             if propagated_relations:
       
    52                 for relation in propagated_relations:
       
    53                     relations.append(relation)
       
    54             return relations
       
    55         except exceptions.ParseError:
       
    56             return None
       
    57     
       
    58 
       
    59 class ConfigurationContext(rules.DefaultContext):
       
    60     
       
    61     def __init__(self, data):
       
    62         rules.DefaultContext.__init__(self, data)
       
    63         
       
    64         # Callback called with the setting reference when a setting is dereferenced
       
    65         # as a terminal expression
       
    66         self.ref_terminal_callback = None
       
    67         
       
    68         # Callback called with the setting reference when a setting is dereferenced
       
    69         # inside an EvalExpression
       
    70         self.ref_eval_callback = None
       
    71         
       
    72         # Callback called with the setting reference when the value of a setting
       
    73         # is set inside a SetExpression
       
    74         self.ref_set_callback = None
       
    75         
       
    76     def handle_terminal(self, expression):
       
    77         try:
       
    78             value = self.data.get_default_view().get_feature(expression).get_value()
       
    79             
       
    80             # Got a valid ref, call the callback
       
    81             if self.ref_terminal_callback:
       
    82                 self.ref_terminal_callback(expression)
       
    83             
       
    84             return value
       
    85         except exceptions.NotFound,e:
       
    86             """ return the expression itself if it is not a fearef """
       
    87             #print "handle_terminal constant %s" % (expression)
       
    88             try:
       
    89                 return eval(expression)
       
    90             except (NameError,SyntaxError), e:
       
    91                 return expression
       
    92 
       
    93     def eval(self, ast, expression, value):
       
    94         #print "expression %s = %s" % (expression,value)
       
    95         pass
       
    96         
       
    97 class ConfigurationBaseRelation(rules.BaseRelation):
       
    98     def __init__(self, data, left, right):
       
    99         self.context = ConfigurationContext(data)
       
   100         super(ConfigurationBaseRelation, self).__init__(data, left, right)
       
   101 
       
   102 class RequireRelation(ConfigurationBaseRelation):
       
   103     KEY = 'requires'
       
   104     def __init__(self, data, left, right):
       
   105         super(RequireRelation, self).__init__(data, left, right)
       
   106         self.context = ConfigurationContext(data)
       
   107 
       
   108 class ConfigureRelation(ConfigurationBaseRelation):
       
   109     KEY = 'configures'
       
   110     def __init__(self, data, left, right):
       
   111         self.context = ConfigurationContext(data)
       
   112         super(ConfigureRelation, self).__init__(data, left, right)
       
   113         
       
   114         # A plugin.RelationExecutionResult object is stored here
       
   115         self._execution_result = None
       
   116         
       
   117     
       
   118     def execute(self, context=None):
       
   119         self._execution_result = None
       
   120         exec_results = []
       
   121         
       
   122         result = rules.BaseRelation.execute(self, context)
       
   123         
       
   124         if len(exec_results) > 0:
       
   125             # There should be only one ConfigureExpression inside a ConfigureRelation
       
   126             if len(exec_results) > 1:
       
   127                 log.warning("Execution of ConfigureRelation returned more than one result, ignoring all except the first")
       
   128             self._execution_result = exec_results[0]
       
   129         
       
   130         return result
       
   131     
       
   132     def get_execution_result(self):
       
   133         """
       
   134         Return the execution result from the most recent call to execute().
       
   135         """
       
   136         return self._execution_result
       
   137 
       
   138 def handle_configure(self, left, right):
       
   139     if left and right:
       
   140         return True
       
   141     elif not left:
       
   142         return True
       
   143     return False
       
   144 
       
   145 def handle_set(self, left, right):
       
   146     left.set_value(right)
       
   147 
       
   148 def handle_filenamejoin(self, left, right):
       
   149     def extract_dirname(path):
       
   150         """Extract directory name (will always contain a trailing slash or backslash)"""
       
   151         pos = max(path.rfind('/'), path.rfind('\\'))
       
   152         if pos == -1:   return path + '/'
       
   153         else:           return path[:pos + 1]
       
   154     
       
   155     def extract_filename(path):
       
   156         pos = max(path.rfind('/'), path.rfind('\\'))
       
   157         if pos == -1:   return path
       
   158         else:           return path[pos + 1:]
       
   159     
       
   160     return extract_dirname(left) + extract_filename(right)
       
   161 
       
   162 def handle_plus(self, left, right):
       
   163     return left + right
       
   164 
       
   165 def handle_minus(self, left, right):
       
   166     return left - right
       
   167 
       
   168 def handle_multiply(self, left, right):
       
   169     return left * right
       
   170 
       
   171 def handle_divide(self, left, right):
       
   172     return left / right
       
   173 
       
   174 class ConfigureExpression(rules.TwoOperatorExpression):
       
   175     PRECEDENCE = rules.PRECEDENCES['RELATION_OPERATORS']
       
   176     KEY = 'configures'
       
   177     OP = handle_configure
       
   178 
       
   179     def eval(self, context, **kwargs):
       
   180         input_refs = []
       
   181         affected_refs = []
       
   182         
       
   183         # Evaluate the left-hand side expression, catching refs for the result
       
   184         try:
       
   185             context.ref_terminal_callback = lambda ref: input_refs.append(ref)
       
   186             context.ref_eval_callback = lambda ref: input_refs.append(ref)
       
   187             evaluated_left = self.left.eval(context, **kwargs)
       
   188         finally:
       
   189             context.ref_terminal_callback = None
       
   190             context.ref_eval_callback = None
       
   191         
       
   192         if evaluated_left:
       
   193             # If left evaluated to True, evaluate the right-hand side and
       
   194             # catch refs from SetExpression evaluations
       
   195             try:
       
   196                 context.ref_set_callback = lambda ref: affected_refs.append(ref)
       
   197                 self.value = self.right.eval(context, **kwargs)
       
   198             finally:
       
   199                 context.ref_set_callback = None
       
   200         else:
       
   201             self.value = True
       
   202         
       
   203         if not self.value:
       
   204             left_keys = []
       
   205             for ref in self.ast.extract_refs(str(self.left)):
       
   206                 for key in context.get_keys(ref):
       
   207                     left_keys.append(key)
       
   208 
       
   209             for key in left_keys:
       
   210                 self.ast.add_error(key, { 'error_string' : 'CONFIGURES right side value is "False"',
       
   211                                           'left_key' : key,
       
   212                                           'rule' : self.ast.expression
       
   213                                           })
       
   214         
       
   215         return self.value
       
   216 
       
   217 class MultiplyExpression(rules.TwoOperatorExpression):
       
   218     expression = "multiply_operation"
       
   219     PRECEDENCE = rules.PRECEDENCES['MULDIV_OPERATORS']
       
   220     KEY= '*'
       
   221     OP = handle_multiply
       
   222 
       
   223 class DivideExpression(rules.TwoOperatorExpression):
       
   224     expression = "divide_operation"
       
   225     PRECEDENCE = rules.PRECEDENCES['MULDIV_OPERATORS']
       
   226     KEY= '/'
       
   227     OP = handle_divide
       
   228 
       
   229 class PlusExpression(rules.TwoOperatorExpression):
       
   230     expression = "plus_operation"
       
   231     PRECEDENCE = rules.PRECEDENCES['ADDSUB_OPERATORS']
       
   232     KEY= '+'
       
   233     OP = handle_plus
       
   234 
       
   235 class MinusExpression(rules.TwoOperatorExpression):
       
   236     expression = "minus_operation"
       
   237     PRECEDENCE = rules.PRECEDENCES['ADDSUB_OPERATORS']
       
   238     KEY= '-'
       
   239     OP = handle_minus
       
   240 
       
   241 class EvalExpression(rules.OneParamExpression):
       
   242     expression = "__eval__"
       
   243     PRECEDENCE = rules.PRECEDENCES['PREFIX_OPERATORS']
       
   244     KEY = '__eval__'
       
   245     
       
   246     def __init__(self, ast, expression):
       
   247         super(EvalExpression, self).__init__(ast, expression)
       
   248         self.expression = expression
       
   249         self._str_to_eval = eval(expression.expression)
       
   250         #self.default_view = default_view
       
   251     
       
   252     def extract_refs(self):
       
   253         result = []
       
   254         result.extend(utils.extract_delimited_tokens(self._str_to_eval, delimiters=('${', '}')))
       
   255         result.extend(utils.extract_delimited_tokens(self._str_to_eval, delimiters=('@{', '}')))
       
   256         return result
       
   257     
       
   258     def eval(self, context, **kwargs):
       
   259         # Using the configuration to pass the eval globals dictionary to here,
       
   260         # since there isn't any easy way to do this more elegantly
       
   261         globals_and_locals = {}
       
   262         if hasattr(context, '_eval_expression_globals_dict'):
       
   263             globals_and_locals = context._eval_expression_globals_dict
       
   264         
       
   265         str_to_eval = self._str_to_eval
       
   266         
       
   267         def expand_feature_ref(ref, index):
       
   268             var_name = "__fea_%05d" % index
       
   269             globals_and_locals[var_name] = context.configuration.get_default_view().get_feature(ref)
       
   270             if context.ref_eval_callback:
       
   271                 context.ref_eval_callback(ref)
       
   272             return var_name
       
   273         def expand_value_ref(ref, index):
       
   274             var_name = "__feaval_%05d" % index
       
   275             globals_and_locals[var_name] = context.configuration.get_default_view().get_feature(ref).get_value()
       
   276             if context.ref_eval_callback:
       
   277                 context.ref_eval_callback(ref)
       
   278             return var_name
       
   279         
       
   280         str_to_eval = utils.expand_delimited_tokens(str_to_eval, expand_feature_ref, delimiters=('@{', '}'))
       
   281         str_to_eval = utils.expand_delimited_tokens(str_to_eval, expand_value_ref, delimiters=('${', '}'))
       
   282         
       
   283         # Strip leading and trailing whitespace to avoid indentation problems
       
   284         str_to_eval = str_to_eval.strip()
       
   285         
       
   286         ret = None
       
   287         
       
   288         try:
       
   289             ret = eval(str_to_eval, globals_and_locals)
       
   290             return ret
       
   291         except SyntaxError, e:
       
   292             logging.getLogger('cone.ruleml').warning("Invalid syntax in eval: %s" % (str_to_eval) )
       
   293             self.ast.add_error(self.expression, { 'error_string' : 'Invalid syntax in eval', 'str_to_eval' : str_to_eval, 'rule' : self.ast.expression })
       
   294         except Exception, e:
       
   295             logging.getLogger('cone.ruleml').warning("Execution failed for eval: %s %s: %s" % (str_to_eval, type(e), e) )
       
   296             self.ast.add_error(self.expression, { 'error_string' : 'Execution failed for eval', 'str_to_eval' : str_to_eval, 'rule' : self.ast.expression })
       
   297 
       
   298 rules.OPERATORS[EvalExpression.KEY] = EvalExpression
       
   299 
       
   300 class FilenamejoinExpression(rules.TwoOperatorExpression):
       
   301     expression = "filenamejoin"
       
   302     PRECEDENCE = rules.PRECEDENCES['ADDSUB_OPERATORS']
       
   303     KEY = 'filenamejoin'
       
   304     OP = handle_filenamejoin
       
   305     
       
   306 rules.OPERATORS[FilenamejoinExpression.KEY] = FilenamejoinExpression
       
   307     
       
   308 class SetExpression(rules.TwoOperatorExpression):
       
   309     PRECEDENCE = rules.PRECEDENCES['SET_OPERATORS']
       
   310     KEY= '='
       
   311     OP = handle_set
       
   312 
       
   313 #    def eval(self, context):
       
   314 #        try:
       
   315 #            variable = context.configuration.get_default_view().get_feature(self.left.expression)
       
   316 #            value = self.right.eval(context)
       
   317 #            variable.set_value(value)
       
   318 #            logging.getLogger('cone.ruleml').info("Set %r = %r from %r" % (self.left.expression, value, self.right.expression) )
       
   319 #            if context.ref_set_callback:
       
   320 #                context.ref_set_callback(self.left.expression)
       
   321 #            return True
       
   322 #        except exceptions.NotFound,e:
       
   323 #            self.ast.add_error(self.left.expression, { 'error_string' : 'Setting value failed, because of %s' % e,
       
   324 #                               'left_key' : self.left.expression,
       
   325 #                               'rule' : self.ast.expression})
       
   326 #            return False
       
   327 
       
   328     def eval(self, context, **kwargs):
       
   329         
       
   330         value = self.right.eval(context, **kwargs)
       
   331         ref = self.left.get_ref()
       
   332         context.set(ref, value, **kwargs)
       
   333         return True
       
   334 
       
   335 _relations_and_operators_backup = None
       
   336 
       
   337 def register():
       
   338     """
       
   339     Register the relations and operators to ConE rules.
       
   340     """
       
   341     global _relations_and_operators_backup
       
   342     if _relations_and_operators_backup is None:
       
   343         # Create the backup copies of the dictionaries
       
   344         rels_backup = rules.RELATIONS.copy()
       
   345         ops_backup = rules.OPERATORS.copy()
       
   346         assert rels_backup is not rules.RELATIONS
       
   347         assert ops_backup is not rules.OPERATORS
       
   348         _relations_and_operators_backup = (rels_backup, ops_backup)
       
   349         
       
   350         # Register relations and operators to rules
       
   351         rules.RELATIONS[RequireRelation.KEY] = RequireRelation
       
   352         rules.RELATIONS[ConfigureRelation.KEY] = ConfigureRelation
       
   353         rules.OPERATORS[ConfigureExpression.KEY] = ConfigureExpression
       
   354         rules.OPERATORS[PlusExpression.KEY] = PlusExpression
       
   355         rules.OPERATORS[SetExpression.KEY] = SetExpression
       
   356         rules.OPERATORS[MinusExpression.KEY] = MinusExpression
       
   357         rules.OPERATORS[MultiplyExpression.KEY] = MultiplyExpression
       
   358         rules.OPERATORS[DivideExpression.KEY] = DivideExpression
       
   359 
       
   360 def unregister():
       
   361     """
       
   362     Undo the changes made by a call to register().
       
   363     """
       
   364     global _relations_and_operators_backup
       
   365     if _relations_and_operators_backup is not None:
       
   366         rules.RELATIONS = _relations_and_operators_backup[0]
       
   367         rules.OPERATORS = _relations_and_operators_backup[1]
       
   368         _relations_and_operators_backup = None
       
   369 
       
   370 def parse_rule(rulestring):
       
   371     """
       
   372     Divide the given rule string into (left side, relation, right side) components. 
       
   373     @return: Triple (left side, relation, right side)
       
   374     """
       
   375     left_expression = ''
       
   376     relation_name = None
       
   377     right_expression = ''
       
   378     for token in rules.get_tokens(rulestring):
       
   379         if relation_name == None:
       
   380             if token in rules.RELATIONS.keys():
       
   381                 relation_name = token
       
   382             else:
       
   383                 left_expression += ' ' + token
       
   384         else:
       
   385             right_expression += ' ' + token
       
   386     
       
   387     if relation_name == None:
       
   388         raise exceptions.ParseError('invalid rule definition %s' % rulestring)
       
   389     
       
   390     return (left_expression,relation_name,right_expression)
       
   391