#
# 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:
#
'''
implementation for ruleml relations.
'''
import os
import StringIO
import logging
import operator as ops
import re
import sys, traceback
log = logging.getLogger('cone.ruleplugin.relations')
from cone.public import api, rules, utils, exceptions, plugin
class RelationFactory(api.FactoryBase):
@ classmethod
def get_relation_by_name(cls, relation_name):
"""
Get the class name by file extension.
"""
try:
return rules.RELATIONS.get(relation_name)
except KeyError:
raise exceptions.NotSupportedException("No Relation class found for name %s" % relation_name)
@ classmethod
def get_relations(cls, relation):
try:
relations = []
(left_expression,relation_name,right_expression) = parse_rule(relation)
relation = cls.get_relation_by_name(relation_name)(left_expression, right_expression)
relations.append(relation)
propagated_relations = cls.get_relations(right_expression)
if propagated_relations:
for relation in propagated_relations:
relations.append(relation)
return relations
except exceptions.ParseError:
return None
class ConfigurationBaseRelation(rules.BaseRelation):
def __init__(self, left, right):
super(ConfigurationBaseRelation, self).__init__(None, left, right)
self.context = None
class RequireRelation(ConfigurationBaseRelation):
KEY = 'requires'
relation_name = 'requires'
def __init__(self, left, right):
super(RequireRelation, self).__init__(left, right)
class ConfigureRelation(ConfigurationBaseRelation):
KEY = 'configures'
relation_name = 'configures'
def __init__(self, left, right):
super(ConfigureRelation, self).__init__(left, right)
# A plugin.RelationExecutionResult object is stored here
self._execution_result = None
def execute(self, context):
self._execution_result = None
exec_results = []
result = rules.BaseRelation.execute(self, context)
if len(exec_results) > 0:
# There should be only one ConfigureExpression inside a ConfigureRelation
if len(exec_results) > 1:
log.warning("Execution of ConfigureRelation returned more than one result, ignoring all except the first")
self._execution_result = exec_results[0]
return result
def get_execution_result(self):
"""
Return the execution result from the most recent call to execute().
"""
return self._execution_result
def handle_configure(self, left, right):
if left and right:
return True
elif not left:
return True
return False
def handle_filenamejoin(self, left, right):
def extract_dirname(path):
"""Extract directory name (will always contain a trailing slash or backslash)"""
pos = max(path.rfind('/'), path.rfind('\\'))
if pos == -1: return path + '/'
else: return path[:pos + 1]
def extract_filename(path):
pos = max(path.rfind('/'), path.rfind('\\'))
if pos == -1: return path
else: return path[pos + 1:]
return extract_dirname(left) + extract_filename(right)
class ConfigureExpression(rules.TwoOperatorExpression):
PRECEDENCE = rules.PRECEDENCES['RELATION_OPERATORS']
KEY = 'configures'
OP = handle_configure
def eval(self, context, **kwargs):
input_refs = []
affected_refs = []
# Evaluate the left-hand side expression
evaluated_left = self.left.eval(context, **kwargs)
if evaluated_left:
# If left evaluated to True, evaluate the right-hand side
self.value = self.right.eval(context, **kwargs)
else:
self.value = True
if not self.value:
left_keys = []
for ref in self.ast.extract_refs(str(self.left)):
for key in context.get_keys(ref):
left_keys.append(key)
for key in left_keys:
self.ast.add_error(key, { 'error_string' : 'CONFIGURES right side value is "False"',
'left_key' : key,
'rule' : self.ast.expression
})
return self.value
class EvalExpression(rules.OneParamExpression):
expression = "__eval__"
PRECEDENCE = rules.PRECEDENCES['PREFIX_OPERATORS']
KEY = '__eval__'
def __init__(self, ast, expression):
super(EvalExpression, self).__init__(ast, expression)
self.expression = expression
self._str_to_eval = eval(expression.expression)
#self.default_view = default_view
def extract_refs(self):
result = []
result.extend(utils.extract_delimited_tokens(self._str_to_eval, delimiters=('${', '}')))
result.extend(utils.extract_delimited_tokens(self._str_to_eval, delimiters=('@{', '}')))
return result
def get_refs(self):
return self.extract_refs()
def eval(self, context, **kwargs):
# Using the configuration to pass the eval globals dictionary to here,
# since there isn't any easy way to do this more elegantly
globals_and_locals = {}
if hasattr(context, '_eval_expression_globals_dict'):
globals_and_locals = context._eval_expression_globals_dict
str_to_eval = self._str_to_eval
def expand_feature_ref(ref, index):
var_name = "__fea_%05d" % index
globals_and_locals[var_name] = context.configuration.get_default_view().get_feature(ref)
return var_name
def expand_value_ref(ref, index):
var_name = "__feaval_%05d" % index
globals_and_locals[var_name] = context.configuration.get_default_view().get_feature(ref).get_value()
return var_name
str_to_eval = utils.expand_delimited_tokens(str_to_eval, expand_feature_ref, delimiters=('@{', '}'))
str_to_eval = utils.expand_delimited_tokens(str_to_eval, expand_value_ref, delimiters=('${', '}'))
# Strip leading and trailing whitespace to avoid indentation problems
str_to_eval = str_to_eval.strip()
ret = None
try:
ret = eval(str_to_eval, globals_and_locals)
return ret
except SyntaxError, e:
logging.getLogger('cone.ruleml').warning("Invalid syntax in eval: %s" % (str_to_eval) )
self.ast.add_error(self.expression, { 'error_string' : 'Invalid syntax in eval', 'str_to_eval' : str_to_eval, 'rule' : self.ast.expression })
except Exception, e:
logging.getLogger('cone.ruleml').warning("Execution failed for eval: %s %s: %s" % (str_to_eval, type(e), e) )
self.ast.add_error(self.expression, { 'error_string' : 'Execution failed for eval', 'str_to_eval' : str_to_eval, 'rule' : self.ast.expression })
class FilenamejoinExpression(rules.TwoOperatorExpression):
expression = "filenamejoin"
PRECEDENCE = rules.PRECEDENCES['ADDSUB_OPERATORS']
KEY = 'filenamejoin'
OP = handle_filenamejoin
# Register relations and operators to rules
rules.RELATIONS[RequireRelation.KEY] = RequireRelation
rules.RELATIONS[ConfigureRelation.KEY] = ConfigureRelation
rules.OPERATORS[FilenamejoinExpression.KEY] = FilenamejoinExpression
rules.OPERATORS[EvalExpression.KEY] = EvalExpression
rules.OPERATORS[ConfigureExpression.KEY] = ConfigureExpression
def parse_rule(rulestring):
"""
Divide the given rule string into (left side, relation, right side) components.
@return: Triple (left side, relation, right side)
"""
left_expression = ''
relation_name = None
right_expression = ''
for token in rules.get_tokens(rulestring):
if relation_name == None:
if token in rules.RELATIONS.keys():
relation_name = token
else:
left_expression += ' ' + token
else:
right_expression += ' ' + token
if relation_name == None:
raise exceptions.ParseError('invalid rule definition %s' % rulestring)
return (left_expression,relation_name,right_expression)