17 A plugin implementation for rule generation. |
17 A plugin implementation for rule generation. |
18 ''' |
18 ''' |
19 |
19 |
20 |
20 |
21 import os |
21 import os |
22 import sys |
|
23 import logging |
22 import logging |
24 import shutil |
23 import pkg_resources |
25 |
|
26 import __init__ |
|
27 import re |
|
28 |
24 |
29 from ruleplugin import relations |
25 from ruleplugin import relations |
30 from cone.public import exceptions,plugin,utils,api,rules |
26 from cone.public import plugin,utils,rules |
31 |
27 |
32 class RuleImpl(plugin.ImplBase): |
28 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' |
29 IMPL_TYPE_ID = 'ruleml' |
38 DEFAULT_INVOCATION_PHASE = 'pre' |
30 DEFAULT_INVOCATION_PHASE = 'pre' |
39 |
31 |
40 def __init__(self, ref, configuration, relation_container): |
32 def __init__(self, ref, configuration, relation_container): |
41 """ |
33 """ |
42 Overloading the default constructor |
34 Overloading the default constructor |
43 """ |
35 """ |
44 plugin.ImplBase.__init__(self,ref,configuration) |
36 plugin.ImplBase.__init__(self,ref,configuration) |
45 self.logger = logging.getLogger('cone.ruleml(%s)' % self.ref) |
|
46 self.relation_container = relation_container |
37 self.relation_container = relation_container |
47 |
38 |
48 def list_output_files(self): |
39 def list_output_files(self): |
49 """ |
40 """ |
50 Return a list of output files as an array. |
41 Return a list of output files as an array. |
51 """ |
42 """ |
52 return [] |
43 return [] |
53 |
44 |
|
45 def get_refs(self): |
|
46 """ |
|
47 Return a list of all ConfML setting references that affect this |
|
48 implementation. May also return None if references are not relevant |
|
49 for the implementation. |
|
50 """ |
|
51 refs = [] |
|
52 relations = self.get_relations() |
|
53 for relation in relations: |
|
54 # get refs from relation return a tuple (left side refs, right side refs) |
|
55 # only the left side refs are the "input" refs |
|
56 refs += relation.get_refs() |
|
57 # If the rules do not have any references return None to disable filter by refs |
|
58 if refs == []: |
|
59 refs = None |
|
60 return refs |
|
61 |
|
62 def get_target_refs(self): |
|
63 """ |
|
64 Return a list of all ConfML setting references that are affected by this |
|
65 implementation. May also return None if references are not relevant |
|
66 for the implementation. |
|
67 """ |
|
68 refs = [] |
|
69 relations = self.get_relations() |
|
70 for relation in relations: |
|
71 refs += relation.get_set_refs() |
|
72 return refs |
|
73 |
|
74 def get_outputs(self): |
|
75 """ |
|
76 Return a list of GenerationOutput objets as a list. |
|
77 """ |
|
78 outputs = [] |
|
79 phase = None |
|
80 if self.generation_context: phase = self.generation_context.phase |
|
81 for rel in self.get_relations(): |
|
82 outrefs = rel.get_set_refs() |
|
83 for ref in outrefs: |
|
84 outputs.append(plugin.GenerationOutput(ref,rel,type='ref', phase=phase)) |
|
85 return outputs |
|
86 |
54 def generate(self, context=None): |
87 def generate(self, context=None): |
55 self.logger.info("Generating rules from %s" % self.ref) |
88 logging.getLogger('cone.ruleml(%s)' % self.ref).info("Generating rules from %s" % self.ref) |
56 relation_container = self.get_relation_container() |
89 relation_container = self.get_relation_container() |
57 relation_container.context = context |
90 relation_container.context = context |
58 return relation_container.execute() |
91 return relation_container.execute(context) |
59 |
|
60 def has_tag(self, tags, policy=None): |
|
61 # RuleML should always be executed, regardless of the tags |
|
62 return True |
|
63 |
92 |
64 def get_relation_container(self): |
93 def get_relation_container(self): |
65 return self.relation_container |
94 return self.relation_container |
|
95 |
|
96 def get_relations(self): |
|
97 return self.relation_container.get_relations() |
|
98 |
|
99 class RuleBuiltinsModule(object): |
|
100 pass |
66 |
101 |
67 class RulemlRelationContainer(plugin.RelationContainer): |
102 class RulemlRelationContainer(plugin.RelationContainer): |
68 """ |
103 """ |
69 Relation container for RuleML rules. |
104 Relation container for RuleML rules. |
70 |
105 |
71 Basically this is a wrapper for rules.RelationContainer that adapts |
106 Basically this is a wrapper for rules.RelationContainer that adapts |
72 it to the interface of plugin.RelationContainer. |
107 it to the interface of plugin.RelationContainer. |
73 """ |
108 """ |
74 def __init__(self, configuration, source, rule_list, eval_globals): |
109 def __init__(self, configuration, source, rule_list, eval_globals): |
75 plugin.RelationContainer.__init__(self, configuration, source=source) |
110 plugin.RelationContainer.__init__(self, configuration, source=source) |
76 self.logger = logging.getLogger('cone.ruleml_relation_container(%s)' % self.source) |
|
77 self.configuration = configuration |
111 self.configuration = configuration |
78 self.relation_container = rules.RelationContainerImpl() |
112 self.relation_container = rules.RelationContainerImpl() |
79 self.eval_globals = eval_globals |
113 self.eval_globals = eval_globals |
80 self.context = None |
114 self.context = None |
81 for rule in rule_list: |
115 for rule in rule_list: |
82 self.relation_container.add_relation(rule) |
116 self.relation_container.add_relation(rule) |
83 |
117 |
84 def execute(self): |
118 def execute(self, context=None): |
85 results = [] |
119 results = [] |
86 |
120 if context: self.context = context |
|
121 |
87 # Create the autoconfig if not done already |
122 # Create the autoconfig if not done already |
88 plugin.get_autoconfig(self.configuration) |
123 autoconfig = plugin.get_autoconfig(self.configuration) |
89 |
124 |
90 # Register relations etc. to the rule engine. |
125 # Using the configuration to pass the eval globals dict to the |
91 # Due to unit test issues the relations are not registered |
126 # eval expression. The configuration only contains the globals |
92 # in the relations module, but only for the duration of |
127 # dict for the duration of the rule execution, so hopefully this |
93 # rule parsing and execution |
128 # shouldn't mess anything up |
94 relations.register() |
129 self._set_builtin_eval_globals() |
95 try: |
130 self.context._eval_expression_globals_dict = self.eval_globals |
96 # Using the configuration to pass the eval globals dict to the |
131 for i, rel in enumerate(self.relation_container): |
97 # eval expression. The configuration only contains the globals |
132 index = i + 1 |
98 # dict for the duration of the rule execution, so hopefully this |
133 # Execute |
99 # shouldn't mess anything up |
134 self._execute_relation_and_log_error(rel, self.source, index, context) |
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 |
135 |
116 del self.configuration._eval_expression_globals_dict |
136 del self.context._eval_expression_globals_dict |
117 |
137 |
118 if self.relation_container.has_errors(): |
138 if self.relation_container.has_errors(): |
119 for error in self.relation_container.get_errors(): |
139 for error in self.relation_container.get_errors(): |
120 self.logger.error(error) |
140 logging.getLogger('cone.ruleml_relation_container(%s)' % self.source).error(error) |
121 |
141 |
122 if self.context: |
142 if self.context: |
123 self.context.results += results |
143 self.context.results += results |
124 return results |
144 return results |
125 finally: |
|
126 relations.unregister() |
|
127 |
145 |
128 def get_relation_count(self): |
146 def get_relation_count(self): |
129 return len(self.relation_container) |
147 return len(self.relation_container) |
130 |
148 |
|
149 def get_relations(self): |
|
150 return list(self.relation_container) |
|
151 |
131 def _set_builtin_eval_globals(self): |
152 def _set_builtin_eval_globals(self): |
132 """ |
153 """ |
133 Add built-in attributes into the eval globals dictionary. |
154 Add built-in attributes into the eval globals dictionary. |
134 """ |
155 """ |
135 class RuleBuiltinsModule(object): |
156 builtins = RuleBuiltinsModule() |
136 pass |
|
137 |
|
138 builtins = RuleBuiltinsModule() |
|
139 builtins.configuration = self.configuration |
157 builtins.configuration = self.configuration |
|
158 builtins.context = self.context |
140 |
159 |
141 self.eval_globals['ruleml'] = builtins |
160 self.eval_globals['ruleml'] = builtins |
142 |
161 |
143 class RuleImplReaderBase(plugin.ReaderBase): |
162 class RuleImplReader(plugin.ReaderBase): |
144 NAMESPACE = None # Used as a base class, so should have no namespace |
163 NAMESPACE = 'http://www.s60.com/xml/ruleml/3' |
|
164 NAMESPACE_ID = 'ruleml3' |
|
165 ROOT_ELEMENT_NAME = 'ruleml' |
145 FILE_EXTENSIONS = ['ruleml'] |
166 FILE_EXTENSIONS = ['ruleml'] |
146 |
167 |
147 def __init__(self, resource_ref, configuration): |
168 def __init__(self, resource_ref, configuration): |
148 self.resource_ref = resource_ref |
169 self.resource_ref = resource_ref |
149 self.configuration = configuration |
170 self.configuration = configuration |
150 self.logger = logging.getLogger('cone.ruleml(%s)' % self.resource_ref) |
|
151 |
171 |
152 @classmethod |
172 @classmethod |
153 def read_impl(cls, resource_ref, configuration, etree): |
173 def read_impl(cls, resource_ref, configuration, etree): |
154 reader = cls(resource_ref, configuration) |
174 reader = cls(resource_ref, configuration) |
155 |
175 rules = reader.parse_rules(resource_ref, etree) |
156 # Register relations etc. to the rule engine. |
176 eval_globals = reader.parse_eval_globals(etree) |
157 # Due to unit test issues the relations are not registered |
177 lineno = utils.etree.get_lineno(etree) |
158 # in the relations module, but only for the duration of |
178 |
159 # rule parsing and execution |
179 # Create an ImplContainer to hold each rule as its own |
160 relations.register() |
180 # RuleML implementation |
161 try: |
181 main_impl = plugin.ImplContainer(resource_ref, configuration) |
162 rules = reader.parse_rules(etree) |
182 main_impl.lineno = lineno |
163 eval_globals = reader.parse_eval_globals(etree) |
183 |
164 |
184 for rule in rules: |
165 relation_container = RulemlRelationContainer( |
185 relation_container = RulemlRelationContainer( |
166 configuration = configuration, |
186 configuration = configuration, |
167 source = resource_ref, |
187 source = "%s:%d" % (resource_ref, rule.lineno), |
168 rule_list = rules, |
188 rule_list = [rule], |
169 eval_globals = eval_globals) |
189 eval_globals = eval_globals) |
|
190 |
|
191 impl = RuleImpl(resource_ref, configuration, relation_container) |
|
192 impl.lineno = rule.lineno |
|
193 rule.implml = impl |
170 |
194 |
171 impl = RuleImpl(resource_ref, configuration, relation_container) |
195 main_impl.append(impl) |
172 finally: |
196 return main_impl |
173 relations.unregister() |
197 |
174 |
198 @classmethod |
175 return impl |
199 def get_schema_data(cls): |
176 |
200 return pkg_resources.resource_string('ruleplugin', 'xsd/ruleml3.xsd') |
177 class RuleImplReader1(RuleImplReaderBase): |
201 |
178 NAMESPACE = 'http://www.s60.com/xml/ruleml/1' |
202 def parse_rules(self, ref, etree): |
179 |
|
180 def __init__(self, resource_ref, configuration): |
|
181 RuleImplReaderBase.__init__(self, resource_ref, configuration) |
|
182 |
|
183 def parse_rules(self, etree): |
|
184 rules = [] |
203 rules = [] |
185 for elem in etree.getiterator("{%s}rule" % self.NAMESPACE): |
204 for elem in etree.getiterator("{%s}rule" % self.NAMESPACE): |
186 rules.extend(relations.RelationFactory.get_relations(self.configuration, elem.text)) |
205 lineno = utils.etree.get_lineno(elem) |
187 return rules |
206 rule_str = self._replace_eval_blocks(elem.text or '') |
188 |
207 rels = relations.RelationFactory.get_relations(rule_str) or [] |
189 def parse_eval_globals(self, etree): |
208 for rule in rels: |
190 return {} |
209 rule.ref = ref |
191 |
210 rule.lineno = lineno |
192 class RuleImplReader2(RuleImplReaderBase): |
211 rules.append(rule) |
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 |
212 return rules |
203 |
213 |
204 def parse_eval_globals(self, etree): |
214 def parse_eval_globals(self, etree): |
205 eval_globals = {} |
215 eval_globals = {} |
206 for elem in etree.getiterator("{%s}eval_globals" % self.NAMESPACE): |
216 for elem in etree.getiterator("{%s}eval_globals" % self.NAMESPACE): |