Fix coding rule
[platform/core/api/context.git] / src / trigger / CustomTemplate.cpp
1 /*
2  * Copyright (c) 2017 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 <regex>
18 #include <context_trigger.h>
19 #include "CustomTemplate.h"
20
21 #define CT_KEY_MIN "minimum"
22 #define CT_KEY_MAX "maximum"
23 #define CT_KEY_TYPE "type"
24 #define CT_TYPE_INTEGER "integer"
25 #define CT_TYPE_STRING "string"
26 #define CT_TYPE_ENUM "enum"
27 #define CT_TYPE_DOUBLE "double"
28
29 static std::list<CustomTemplate> __instances;
30
31 CustomTemplate::CustomTemplate(const std::string& name, const Json::Value& templateJson) :
32         __name(name),
33         __templateJson(templateJson)
34 {
35 }
36
37 const std::string& CustomTemplate::getName() const
38 {
39         return __name;
40 }
41
42 int CustomTemplate::match(const std::string& fact)
43 {
44         // true if the given fact is valid w.r.t. the template
45         Json::Reader reader;
46         Json::Value factJson;
47
48         // Error: Invalid Json
49         if (!reader.parse(fact, factJson)) {
50                 _E("Fact: invalid json");
51                 return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER;
52         }
53
54         // Error: Invalid fact
55         IF_FAIL_RETURN_TAG(isValidFact(__templateJson, factJson), CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "Invalid fact");
56
57         return CONTEXT_TRIGGER_ERROR_NONE;
58 }
59
60 bool CustomTemplate::isValidFact(const Json::Value& tmplJson, const Json::Value& factJson)
61 {
62         Json::Value::Members keys = factJson.getMemberNames();
63
64         std::string dataType;
65         for (auto& key : keys) {
66                 // Get type
67                 if (tmplJson[key].isMember(CT_TYPE_ENUM)) {
68                         dataType = CT_TYPE_ENUM;
69                 } else if (tmplJson[key].isMember(CT_KEY_TYPE)) {
70                         dataType = tmplJson[key][CT_KEY_TYPE].asString();
71                 }
72
73                 if (dataType == CT_TYPE_INTEGER) {
74                         IF_FAIL_RETURN_TAG(factJson[key].isInt(), false, _E, "Custom fact: Invalid data type");
75
76                         int val = factJson[key].asInt();
77                         if (tmplJson[key].isMember(CT_KEY_MIN)) {
78                                 IF_FAIL_RETURN_TAG(val >= tmplJson[key][CT_KEY_MIN].asInt(), false, _E, "Custom fact: Invalid value");
79                         }
80                         if (tmplJson[key].isMember(CT_KEY_MAX)) {
81                                 IF_FAIL_RETURN_TAG(val <= tmplJson[key][CT_KEY_MAX].asInt(), false, _E, "Custom fact: Invalid value");
82                         }
83                 } else if (dataType == CT_TYPE_STRING) {
84                         IF_FAIL_RETURN_TAG(factJson[key].isString(), false, _E, "Custom fact: Invalid data type");
85                 } else if (dataType == CT_TYPE_ENUM) {
86                         IF_FAIL_RETURN_TAG(factJson[key].isString(), false, _E, "Custom fact: Invalid data type");
87
88                         std::string val = factJson[key].asString();
89
90                         bool found = false;
91                         Json::Value::Members tmplValues;
92                         for (auto& tmplValue : tmplValues) {
93                                 if (tmplValue == val)
94                                         found = true;
95                         }
96
97                         IF_FAIL_RETURN_TAG(found, false, _E, "Custom fact: Invalid value");
98                 } else {
99                         _E("Custom fact: Invalid data type");
100                         return false;
101                 }
102         }
103         return true;
104 }
105
106 bool CustomTemplate::isValidTemplate(const Json::Value& tmplJson)
107 {
108         bool success = false;
109         Json::Value::Members keys = tmplJson.getMemberNames();
110
111         std::string dataType;
112         for (auto& key : keys) {
113                 // Get type
114                 if (tmplJson[key].isMember(CT_TYPE_ENUM)) {
115                         success = tmplJson[key][CT_TYPE_ENUM].isArray();
116                         IF_FAIL_RETURN_TAG(success, false, _E, "Invalid template");
117                         dataType = CT_TYPE_ENUM;
118                 } else if (tmplJson[key].isMember(CT_KEY_TYPE)) {
119                         dataType = tmplJson[key][CT_KEY_TYPE].asString();
120                         IF_FAIL_RETURN_TAG(dataType == CT_TYPE_INTEGER || dataType == CT_TYPE_STRING,
121                                 false, _E, "Invalid template");
122                 }
123
124                 if (dataType == CT_TYPE_INTEGER) {
125                         success = checkTemplateInt(tmplJson[key]);
126                         IF_FAIL_RETURN(success, false);
127                 } else if (dataType == CT_TYPE_STRING) {
128                         success = checkTemplateString(tmplJson[key]);
129                         IF_FAIL_RETURN(success, false);
130                 } else if (dataType == CT_TYPE_ENUM) {
131                         success = checkTemplateEnum(tmplJson[key]);
132                         IF_FAIL_RETURN(success, false);
133                 }
134         }
135
136         return true;
137 }
138
139 bool CustomTemplate::checkTemplateInt(const Json::Value& elem)
140 {
141         bool min = false;
142         bool max = false;
143         int minVal = 0;
144         int maxVal = 0;
145
146         Json::Value::Members elemKeys = elem.getMemberNames();
147
148         for (auto& elemKey : elemKeys) {
149                 if (elemKey == CT_KEY_MIN) {
150                         min = true;
151                         minVal = elem[elemKey].asInt();
152                 } else if (elemKey == CT_KEY_MAX) {
153                         max = true;
154                         maxVal = elem[elemKey].asInt();
155                 } else {
156                         IF_FAIL_RETURN_TAG(elemKey == CT_KEY_TYPE, false, _E, "invalid key");
157                 }
158         }
159
160         if (min && max) {
161                 IF_FAIL_RETURN_TAG(minVal <= maxVal, false, _E, "Invalid min, max value");
162         }
163
164         return true;
165 }
166
167 bool CustomTemplate::checkTemplateString(const Json::Value& elem)
168 {
169         Json::Value::Members elemKeys = elem.getMemberNames();
170
171         for (auto& elemKey : elemKeys) {
172                 IF_FAIL_RETURN_TAG(elemKey == CT_KEY_TYPE, false, _E, "invalid key");
173         }
174
175         return true;
176 }
177
178 bool CustomTemplate::checkTemplateEnum(const Json::Value& elem)
179 {
180         Json::Value::Members elemKeys = elem.getMemberNames();
181
182         for (auto& elemKey : elemKeys) {
183                 if (elemKey == CT_TYPE_ENUM) {
184                         IF_FAIL_RETURN_TAG(!elem[CT_TYPE_ENUM].empty(), false, _E, "Invalid enum");
185
186                         for (unsigned int i = 0; i < elem[CT_TYPE_ENUM].size(); i++) {
187                                 IF_FAIL_RETURN_TAG(elem[CT_TYPE_ENUM][i].isString(), false, _E, "Enum value sholud be string");
188                         }
189                 } else {
190                         IF_FAIL_RETURN_TAG(elemKey == CT_KEY_TYPE, false, _E, "invalid key");
191                 }
192         }
193
194         return true;
195 }
196
197 int CustomTemplate::add(const std::string& name, const std::string& attrTmpl)
198 {
199         static std::regex nameRegex(R"~(^[\w-\._\/]+$)~", std::regex::optimize);
200         IF_FAIL_RETURN_TAG(std::regex_match(name, nameRegex), CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER, _E, "Invalid name");
201
202         Json::Reader reader;
203         Json::Value tmplJson;
204
205         // Error: Invalid Json
206         if (!reader.parse(attrTmpl, tmplJson)) {
207                 _E("Template: invalid json");
208                 return CONTEXT_TRIGGER_ERROR_INVALID_PARAMETER;
209         }
210
211         // Error: already exist
212         CustomTemplate* tmpl = get(name);
213         if (tmpl) {
214                 if (tmplJson == tmpl->__templateJson) {
215                         return CONTEXT_TRIGGER_ERROR_NONE;
216                 } else {
217                         _E("Template already exists");
218                         return CONTEXT_TRIGGER_ERROR_DATA_EXIST;
219                 }
220         }
221
222         // Error: Invalid template
223         IF_FAIL_RETURN_TAG(isValidTemplate(tmplJson), CONTEXT_TRIGGER_ERROR_INVALID_DATA, _E, "Invalid template");
224
225         __instances.emplace_back(name, tmplJson);
226
227         return CONTEXT_TRIGGER_ERROR_NONE;
228 }
229
230 void CustomTemplate::remove(const std::string& name)
231 {
232         __instances.remove_if(
233                         [&name](const CustomTemplate& tmpl)->bool {
234                                 return tmpl.getName() == name;
235                         });
236 }
237
238 CustomTemplate* CustomTemplate::get(const std::string& name)
239 {
240         for (auto& tmpl : __instances) {
241                 if (tmpl.getName() == name)
242                         return &tmpl;
243         }
244
245         return NULL;
246 }