|
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 A plugin implementation for rule generation. |
|
18 ''' |
|
19 |
|
20 |
|
21 import os |
|
22 import sys |
|
23 import logging |
|
24 import shutil |
|
25 |
|
26 import __init__ |
|
27 import re |
|
28 |
|
29 from ruleplugin import relations |
|
30 from cone.public import exceptions,plugin,utils,api,rules |
|
31 |
|
32 class RuleImpl(plugin.ImplBase): |
|
33 """ |
|
34 MakeImpl plugin finds feature references that are configured in a .ruleml file |
|
35 and generate a rule from them |
|
36 """ |
|
37 IMPL_TYPE_ID = 'ruleml' |
|
38 DEFAULT_INVOCATION_PHASE = 'pre' |
|
39 |
|
40 def __init__(self, ref, configuration, relation_container): |
|
41 """ |
|
42 Overloading the default constructor |
|
43 """ |
|
44 plugin.ImplBase.__init__(self,ref,configuration) |
|
45 self.logger = logging.getLogger('cone.ruleml(%s)' % self.ref) |
|
46 self.relation_container = relation_container |
|
47 |
|
48 def list_output_files(self): |
|
49 """ |
|
50 Return a list of output files as an array. |
|
51 """ |
|
52 return [] |
|
53 |
|
54 def generate(self, context=None): |
|
55 self.logger.info("Generating rules from %s" % self.ref) |
|
56 relation_container = self.get_relation_container() |
|
57 relation_container.context = context |
|
58 return relation_container.execute() |
|
59 |
|
60 def has_tag(self, tags, policy=None): |
|
61 # RuleML should always be executed, regardless of the tags |
|
62 return True |
|
63 |
|
64 def get_relation_container(self): |
|
65 return self.relation_container |
|
66 |
|
67 class RulemlRelationContainer(plugin.RelationContainer): |
|
68 """ |
|
69 Relation container for RuleML rules. |
|
70 |
|
71 Basically this is a wrapper for rules.RelationContainer that adapts |
|
72 it to the interface of plugin.RelationContainer. |
|
73 """ |
|
74 def __init__(self, configuration, source, rule_list, eval_globals): |
|
75 plugin.RelationContainer.__init__(self, configuration, source=source) |
|
76 self.logger = logging.getLogger('cone.ruleml_relation_container(%s)' % self.source) |
|
77 self.configuration = configuration |
|
78 self.relation_container = rules.RelationContainerImpl() |
|
79 self.eval_globals = eval_globals |
|
80 self.context = None |
|
81 for rule in rule_list: |
|
82 self.relation_container.add_relation(rule) |
|
83 |
|
84 def execute(self): |
|
85 results = [] |
|
86 |
|
87 # Create the autoconfig if not done already |
|
88 plugin.get_autoconfig(self.configuration) |
|
89 |
|
90 # Register relations etc. to the rule engine. |
|
91 # Due to unit test issues the relations are not registered |
|
92 # in the relations module, but only for the duration of |
|
93 # rule parsing and execution |
|
94 relations.register() |
|
95 try: |
|
96 # Using the configuration to pass the eval globals dict to the |
|
97 # eval expression. The configuration only contains the globals |
|
98 # dict for the duration of the rule execution, so hopefully this |
|
99 # shouldn't mess anything up |
|
100 self._set_builtin_eval_globals() |
|
101 self.configuration._eval_expression_globals_dict = self.eval_globals |
|
102 for i, rel in enumerate(self.relation_container): |
|
103 index = i + 1 |
|
104 |
|
105 # Execute |
|
106 self._execute_relation_and_log_error(rel, self.source, index) |
|
107 |
|
108 # Collect execution result if supported |
|
109 if hasattr(rel, 'get_execution_result'): |
|
110 result = rel.get_execution_result() |
|
111 if isinstance(result, plugin.RelationExecutionResult): |
|
112 result.source = self.source |
|
113 result.index = index |
|
114 results.append(result) |
|
115 |
|
116 del self.configuration._eval_expression_globals_dict |
|
117 |
|
118 if self.relation_container.has_errors(): |
|
119 for error in self.relation_container.get_errors(): |
|
120 self.logger.error(error) |
|
121 |
|
122 if self.context: |
|
123 self.context.results += results |
|
124 return results |
|
125 finally: |
|
126 relations.unregister() |
|
127 |
|
128 def get_relation_count(self): |
|
129 return len(self.relation_container) |
|
130 |
|
131 def _set_builtin_eval_globals(self): |
|
132 """ |
|
133 Add built-in attributes into the eval globals dictionary. |
|
134 """ |
|
135 class RuleBuiltinsModule(object): |
|
136 pass |
|
137 |
|
138 builtins = RuleBuiltinsModule() |
|
139 builtins.configuration = self.configuration |
|
140 |
|
141 self.eval_globals['ruleml'] = builtins |
|
142 |
|
143 class RuleImplReaderBase(plugin.ReaderBase): |
|
144 NAMESPACE = None # Used as a base class, so should have no namespace |
|
145 FILE_EXTENSIONS = ['ruleml'] |
|
146 |
|
147 def __init__(self, resource_ref, configuration): |
|
148 self.resource_ref = resource_ref |
|
149 self.configuration = configuration |
|
150 self.logger = logging.getLogger('cone.ruleml(%s)' % self.resource_ref) |
|
151 |
|
152 @classmethod |
|
153 def read_impl(cls, resource_ref, configuration, etree): |
|
154 reader = cls(resource_ref, configuration) |
|
155 |
|
156 # Register relations etc. to the rule engine. |
|
157 # Due to unit test issues the relations are not registered |
|
158 # in the relations module, but only for the duration of |
|
159 # rule parsing and execution |
|
160 relations.register() |
|
161 try: |
|
162 rules = reader.parse_rules(etree) |
|
163 eval_globals = reader.parse_eval_globals(etree) |
|
164 |
|
165 relation_container = RulemlRelationContainer( |
|
166 configuration = configuration, |
|
167 source = resource_ref, |
|
168 rule_list = rules, |
|
169 eval_globals = eval_globals) |
|
170 |
|
171 impl = RuleImpl(resource_ref, configuration, relation_container) |
|
172 finally: |
|
173 relations.unregister() |
|
174 |
|
175 return impl |
|
176 |
|
177 class RuleImplReader1(RuleImplReaderBase): |
|
178 NAMESPACE = 'http://www.s60.com/xml/ruleml/1' |
|
179 |
|
180 def __init__(self, resource_ref, configuration): |
|
181 RuleImplReaderBase.__init__(self, resource_ref, configuration) |
|
182 |
|
183 def parse_rules(self, etree): |
|
184 rules = [] |
|
185 for elem in etree.getiterator("{%s}rule" % self.NAMESPACE): |
|
186 rules.extend(relations.RelationFactory.get_relations(self.configuration, elem.text)) |
|
187 return rules |
|
188 |
|
189 def parse_eval_globals(self, etree): |
|
190 return {} |
|
191 |
|
192 class RuleImplReader2(RuleImplReaderBase): |
|
193 NAMESPACE = 'http://www.s60.com/xml/ruleml/2' |
|
194 |
|
195 def __init__(self, resource_ref, configuration): |
|
196 RuleImplReaderBase.__init__(self, resource_ref, configuration) |
|
197 |
|
198 def parse_rules(self, etree): |
|
199 rules = [] |
|
200 for elem in etree.getiterator("{%s}rule" % self.NAMESPACE): |
|
201 rules.extend(relations.RelationFactory.get_relations(self.configuration, self._replace_eval_blocks(elem.text))) |
|
202 return rules |
|
203 |
|
204 def parse_eval_globals(self, etree): |
|
205 eval_globals = {} |
|
206 for elem in etree.getiterator("{%s}eval_globals" % self.NAMESPACE): |
|
207 text = "" |
|
208 if elem.get('file') != None: |
|
209 self._read_eval_globals_from_file(elem.get('file'), eval_globals) |
|
210 else: |
|
211 try: |
|
212 # Strip surrounding whitespace, otherwise there might be Python |
|
213 # indentation errors |
|
214 text = elem.text.strip() |
|
215 exec(text, eval_globals) |
|
216 except Exception, e: |
|
217 self.logger.warning('Failed to evaluate eval_globals block, exception: %s' % (e)) |
|
218 return eval_globals |
|
219 |
|
220 def _read_eval_globals_from_file(self, relative_path, eval_globals): |
|
221 # Get the actual path (relative to the current implementation file) |
|
222 base_path = os.path.dirname(self.resource_ref) |
|
223 pyfile_path = os.path.normpath(os.path.join(base_path, relative_path)).replace('\\', '/') |
|
224 # Read the data and execute |
|
225 try: |
|
226 resource = None |
|
227 resource = self.configuration.get_resource(pyfile_path) |
|
228 text = resource.read() |
|
229 exec(text.replace('\r', ''), eval_globals) |
|
230 except Exception, e: |
|
231 self.logger.warning('Cannot import eval file: %s. Exception: %s' % (pyfile_path, e)) |
|
232 finally: |
|
233 if resource is not None: resource.close() |
|
234 |
|
235 |
|
236 @classmethod |
|
237 def _replace_eval_blocks(cls, code): |
|
238 return utils.expand_delimited_tokens( |
|
239 string = code, |
|
240 expander_func = lambda ref, index: '__eval__ %r' % ref, |
|
241 delimiters =('{%', '%}')) |