Remove profile build dependencies
[platform/core/context/context-service.git] / src / trigger / RuleEvaluator.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <Json.h>
18 #include <Types.h>
19 #include <TriggerRuleTypes.h>
20 #include "RuleEvaluator.h"
21 #include "FactTypes.h"
22
23 #define OPERATOR_EQ "=="
24 #define OPERATOR_NEQ "!="
25 #define OPERATOR_LEQ "<="
26 #define OPERATOR_GEQ ">="
27 #define OPERATOR_LT "<"
28 #define OPERATOR_GT ">"
29
30 using namespace ctx;
31 using namespace ctx::trigger;
32
33 RuleEvaluator::RuleEvaluator()
34 {
35 }
36
37 template <typename T>
38 bool RuleEvaluator::__evaluateSingleData(T factVal, Json& comparison, std::string op)
39 {
40         T ruleVal;
41         comparison.get(NULL, TRIG_RULE_KEY_VALUE, &ruleVal);
42
43         if (op == TRIG_RULE_OP_EQUAL_TO) {
44                 return (ruleVal == factVal);
45         } else if (op == TRIG_RULE_OP_NOT_EQUAL_TO) {
46                 return (ruleVal != factVal);
47         } else if (op == TRIG_RULE_OP_GREATER_THAN) {
48                 return (ruleVal > factVal);
49         } else if (op == TRIG_RULE_OP_GREATER_THAN_OR_EQUAL_TO) {
50                 return (ruleVal >= factVal);
51         } else if (op == TRIG_RULE_OP_LESS_THAN) {
52                 return (ruleVal < factVal);
53         } else if (op == TRIG_RULE_OP_LESS_THAN_OR_EQUAL_TO) {
54                 return (ruleVal <= factVal);
55         }
56
57         return false;
58 }
59
60 template <typename T>
61 bool RuleEvaluator::__evaluateDualData(T factVal, Json& comparison, std::string op)
62 {
63         T ruleVal1, ruleVal2;
64         comparison.getAt(NULL, TRIG_RULE_KEY_VALUE, 0, &ruleVal1);
65         comparison.getAt(NULL, TRIG_RULE_KEY_VALUE, 1, &ruleVal2);
66
67         if (op == TRIG_RULE_OP_IN) {
68                 return (ruleVal1 <= factVal && factVal <= ruleVal2);
69         } else if (op == TRIG_RULE_OP_NOT_IN) {
70                 return (factVal < ruleVal1 || ruleVal2 < factVal);
71         }
72
73         return false;
74 }
75
76 template <typename T>
77 bool RuleEvaluator::__evaluateMultipleData(T factVal, Json& comparison, std::string op)
78 {
79         T ruleVal;
80         for (int i = 0; comparison.getAt(NULL, TRIG_RULE_KEY_VALUE, i, &ruleVal); i++) {
81                 if (ruleVal == factVal) {
82                         if (op == TRIG_RULE_OP_ONE_OF) {
83                                 return true;
84                         } else if (op == TRIG_RULE_OP_NONE_OF) {
85                                 return false;
86                         }
87                 }
88         }
89
90         if (op == TRIG_RULE_OP_NONE_OF) {
91                 return true;
92         }
93
94         return false;
95 }
96
97 template <typename T>
98 bool RuleEvaluator::__evaluateData(T factVal, Json& comparison)
99 {
100         std::string op;
101         comparison.get(NULL, TRIG_RULE_KEY_OPERATOR, &op);
102
103         if (op == TRIG_RULE_OP_EQUAL_TO || op == TRIG_RULE_OP_NOT_EQUAL_TO ||
104                 op == TRIG_RULE_OP_GREATER_THAN || op == TRIG_RULE_OP_GREATER_THAN_OR_EQUAL_TO ||
105                 op == TRIG_RULE_OP_LESS_THAN || op == TRIG_RULE_OP_LESS_THAN_OR_EQUAL_TO) {
106                 return __evaluateSingleData(factVal, comparison, op);
107         } else if (op == TRIG_RULE_OP_IN || op == TRIG_RULE_OP_NOT_IN) {
108                 return __evaluateDualData(factVal, comparison, op);
109         } else if (op == TRIG_RULE_OP_ONE_OF || op == TRIG_RULE_OP_NONE_OF) {
110                 return __evaluateMultipleData(factVal, comparison, op);
111         }
112
113     return false;
114 }
115
116 void RuleEvaluator::__replaceSingleDataReferences(Json& eventFactData, Json& ruleComp, std::string& dataKey)
117 {
118         std::string refVal;
119         std::string eventRefStr;
120         int eventRefInt;
121
122         if (!ruleComp.get(dataKey.c_str(), TRIG_RULE_KEY_VALUE, &refVal)) {
123                 return;
124         }
125
126         if (refVal.substr(0, 1) != TRIG_RULE_REF_KEY_PREFIX) {
127                 return;
128         }
129
130         std::string eventKey = refVal.substr(1, refVal.length() - 1);
131         if (eventFactData.get(NULL, eventKey.c_str(), &eventRefStr)) {
132                 ruleComp.set(dataKey.c_str(), TRIG_RULE_KEY_VALUE, eventRefStr);
133         } else if (eventFactData.get(NULL, eventKey.c_str(), &eventRefInt)) {
134                 ruleComp.set(dataKey.c_str(), TRIG_RULE_KEY_VALUE, eventRefInt);
135         } else {
136                 _W("Attribute %s not found in event_data", eventKey.c_str());
137         }
138 }
139
140 void RuleEvaluator::__replaceMultipleDataReferences(Json& eventFactData, Json& ruleComp, std::string& dataKey)
141 {
142         std::string refVal;
143         std::string eventRefStr;
144         int eventRefInt;
145
146         for (int i = 0; ruleComp.getAt(dataKey.c_str(), TRIG_RULE_KEY_VALUE, i, &refVal); i++) {
147                 if (refVal.substr(0, 1) != TRIG_RULE_REF_KEY_PREFIX) {
148                         continue;
149                 }
150
151                 std::string eventKey = refVal.substr(1, refVal.length() - 1);
152                 if (eventFactData.get(NULL, eventKey.c_str(), &eventRefStr)) {
153                         ruleComp.setAt(dataKey.c_str(), TRIG_RULE_KEY_VALUE, i, eventRefStr);
154                 } else if (eventFactData.get(NULL, eventKey.c_str(), &eventRefInt)) {
155                         ruleComp.setAt(dataKey.c_str(), TRIG_RULE_KEY_VALUE, i, eventRefInt);
156                 } else {
157                         _W("Attribute %s not found in event_data", eventKey.c_str());
158                 }
159         }
160 }
161
162 void RuleEvaluator::__replaceDataReferences(Json eventFactData, Json& ruleComp)
163 {
164         // Replace referencing data to actual value
165         std::list<std::string> compKeys;
166         ruleComp.getKeys(&compKeys);
167
168         for (auto it = compKeys.begin(); it != compKeys.end(); ++it) {
169                 std::string dataKey = *it;
170
171                 std::string op;
172                 ruleComp.get(dataKey.c_str(), TRIG_RULE_KEY_OPERATOR, &op);
173
174                 std::string val;
175                 if (op == TRIG_RULE_OP_EQUAL_TO || op == TRIG_RULE_OP_NOT_EQUAL_TO ||
176                         op == TRIG_RULE_OP_GREATER_THAN || op == TRIG_RULE_OP_GREATER_THAN_OR_EQUAL_TO ||
177                         op == TRIG_RULE_OP_LESS_THAN || op == TRIG_RULE_OP_LESS_THAN_OR_EQUAL_TO) {
178
179                         __replaceSingleDataReferences(eventFactData, ruleComp, dataKey);
180                 } else {
181                         __replaceMultipleDataReferences(eventFactData, ruleComp, dataKey);
182                 }
183         }
184 }
185
186 bool RuleEvaluator::__evaluateItem(Json& factItem, Json& ruleItem, std::string logicalOp)
187 {
188         std::string name;
189         factItem.get(NULL, FACT_KEY_NAME, &name);
190
191         Json comparison;
192         ruleItem.get(name.c_str(), TRIG_RULE_KEY_COMPARISON, &comparison);
193
194         std::list<std::string> compKeys;
195         comparison.getKeys(&compKeys);
196         if (compKeys.size() == 0) {
197                 return true;
198         }
199
200         bool isConjunction = (TRIG_RULE_LOGICAL_CONJUNCTION == logicalOp);
201
202         for (auto it = compKeys.begin(); it != compKeys.end(); ++it) {
203                 std::string dataKey = *it;
204
205                 Json comp;
206                 comparison.get(NULL, dataKey.c_str(), &comp);
207
208                 std::string factValStr;
209                 int factValInt;
210
211                 bool result;
212                 if (factItem.get(FACT_KEY_DATA, dataKey.c_str(), &factValStr)) {
213                         result = __evaluateData(factValStr, comp);
214                 } else if (factItem.get(FACT_KEY_DATA, dataKey.c_str(), &factValInt)) {
215                         result = __evaluateData(factValInt, comp);
216                 } else {
217                         _W("Could not get value corresponding to data key %s", dataKey.c_str());
218                         result = false;
219                 }
220
221                 if (isConjunction && !result) {
222                         return false;
223                 } else if (!isConjunction && result) {
224                         return true;
225                 }
226         }
227         return isConjunction;
228 }
229
230 bool RuleEvaluator::__evaluateRuleEvent(Json& fact, Json& rule)
231 {
232         Json factItem;
233         Json ruleItem;
234         fact.get(NULL, FACT_KEY_EVENT, &factItem);
235         rule.get(NULL, TRIG_RULE_KEY_EVENT, &ruleItem);
236
237         std::string eventOp;
238         rule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_EVENT_LOGICAL_OP, &eventOp);
239
240         return __evaluateItem(factItem, ruleItem, eventOp);
241 }
242
243 Json RuleEvaluator::__getConditionFact(Json& fact, Json& ruleCond)
244 {
245         std::list<std::string> condKey;
246         ruleCond.getKeys(&condKey);
247         std::string ruleCondName = *(condKey.begin());
248
249         Json factCond;
250         for (int i = 0; fact.getAt(NULL, FACT_KEY_CONDITION, i, &factCond); i++) {
251                 // Check if fact item name is matched with condition
252                 std::string factCondName;
253                 factCond.get(NULL, FACT_KEY_NAME, &factCondName);
254                 if (factCondName != ruleCondName) {
255                         continue;
256                 }
257
258                 // Check if fact item option is mathced with condition
259                 Json ruleCondOption;
260                 Json factCondOption;
261                 ruleCond.get(ruleCondName.c_str(), TRIG_RULE_KEY_OPTION, &ruleCondOption);
262                 factCond.get(NULL, FACT_KEY_OPTION, &factCondOption);
263                 if (factCondOption == ruleCondOption) {
264                         return factCond;
265                 }
266         }
267
268         _W(YELLOW("find condition failed for condition"));
269         return EMPTY_JSON_OBJECT;
270 }
271
272 bool RuleEvaluator::__evaluateRuleCondition(Json& fact, Json& rule)
273 {
274         std::string ruleOp;
275         rule.get(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_RULE_LOGICAL_OP, &ruleOp);
276         bool isConjunction = (TRIG_RULE_LOGICAL_CONJUNCTION == ruleOp);
277
278         Json ruleCond;
279         for (int i = 0; rule.getAt(NULL, TRIG_RULE_KEY_CONDITION, i, &ruleCond); i++) {
280                 Json factCond = __getConditionFact(fact, ruleCond);
281
282                 bool result;
283                 if (factCond == EMPTY_JSON_OBJECT) {
284                         result = false;
285                 } else {
286                         std::string condOp;
287                         rule.getAt(_TRIG_RULE_KEY_EXTRA, _TRIG_RULE_KEY_CONDITION_LOGICAL_OP, i, &condOp);
288                         result = __evaluateItem(factCond, ruleCond, condOp);
289                 }
290
291                 if (isConjunction && !result) {
292                         return false;
293                 } else if (!isConjunction && result) {
294                         return true;
295                 }
296         }
297
298         return isConjunction;
299 }
300
301 bool RuleEvaluator::__replaceOptionReferences(Json eventFactData, Json& ruleOption)
302 {
303         // Replace referencing option to actual value
304         std::string refVal;
305         std::string eventRefStr;
306         int eventRefInt;
307
308         std::list<std::string> keyList;
309         ruleOption.getKeys(&keyList);
310
311         for (std::list<std::string>::iterator it = keyList.begin(); it != keyList.end(); ++it) {
312                 std::string optionKey = *it;
313
314                 if (!ruleOption.get(NULL, optionKey.c_str(), &refVal)) {
315                         continue;
316                 }
317                 if (!(refVal.substr(0, 1) == TRIG_RULE_REF_KEY_PREFIX)) {
318                         continue;
319                 }
320
321                 std::string eventKey = refVal.substr(1, refVal.length() - 1);
322                 if (eventFactData.get(NULL, eventKey.c_str(), &eventRefStr)) {
323                         ruleOption.set(NULL, optionKey.c_str(), eventRefStr);
324                 } else if (eventFactData.get(NULL, eventKey.c_str(), &eventRefInt)) {
325                         ruleOption.set(NULL, optionKey.c_str(), eventRefInt);
326                 } else {
327                         _W("Option %s not found in event_data", eventKey.c_str());
328                         return false;
329                 }
330         }
331
332         return true;
333 }
334
335 bool RuleEvaluator::__replaceEventReferences(Json& fact, Json& rule)
336 {
337         // Replace referencing data/option to actual value
338         Json eventFactData;
339         if (!fact.get(FACT_KEY_EVENT, FACT_KEY_DATA, &eventFactData)) {
340                 _E("No event data found, error");
341                 return false;
342         }
343
344         Json ruleCond;
345         for (int i = 0; rule.getAt(NULL, TRIG_RULE_KEY_CONDITION, i, &ruleCond); i++) {
346                 std::list<std::string> condKey;
347                 ruleCond.getKeys(&condKey);
348                 std::string ruleCondName = *(condKey.begin());
349
350                 Json ruleComp;
351                 for (int j = 0; ruleCond.getAt(ruleCondName.c_str(), TRIG_RULE_KEY_COMPARISON, j, &ruleComp); j++) {
352                         __replaceDataReferences(eventFactData, ruleComp);
353                 }
354
355                 Json ruleOption;
356                 if (ruleCond.get(ruleCondName.c_str(), TRIG_RULE_KEY_OPTION, &ruleOption)) {
357                         __replaceOptionReferences(eventFactData, ruleOption);
358                 }
359         }
360
361         return true;
362 }
363
364 bool RuleEvaluator::evaluateRule(Json rule, Json fact)
365 {
366         _D("Rule is %s ", rule.str().c_str());
367         _D("fact is %s ", fact.str().c_str());
368
369         RuleEvaluator eval;
370         bool ret;
371         Json tempJson;
372         if (fact.get(NULL, FACT_KEY_CONDITION, &tempJson)) {
373                 Json ruleCopy(rule.str());
374                 if (!eval.__replaceEventReferences(fact, ruleCopy)) {
375                         _W("Replace failed");
376                 }
377                 ret = eval.__evaluateRuleCondition(fact, ruleCopy);
378                 _D("Checking condition %s", ret ? "true" : "false");
379         } else {
380                 ret = eval.__evaluateRuleEvent(fact, rule);
381                 _D("Checking event %s", ret ? "true" : "false");
382         }
383
384         return ret;
385 }