Merge branch 'tizen_5.0' into tizen_5.5
[platform/core/api/webapi-plugins.git] / src / content / content_filter.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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 "content/content_filter.h"
18
19 #include <vector>
20
21 #include "common/converter.h"
22 #include "common/logger.h"
23 #include "common/tools.h"
24
25 using common::AttributeMatchFlag;
26 using common::CompositeFilterType;
27 using common::ErrorCode;
28 using common::JsonCast;
29 using common::PlatformResult;
30
31 namespace extension {
32 namespace content {
33
34 namespace {
35
36 std::map<std::string, std::string> const attributeNameMap = {
37     {"id", "MEDIA_ID"},
38     {"type", "MEDIA_TYPE"},
39     {"mimeType", "MEDIA_MIME_TYPE"},
40     {"name", "MEDIA_DISPLAY_NAME"},
41     {"title", "MEDIA_TITLE"},
42     {"contentURI", "MEDIA_PATH"},
43     {"thumbnailURIs", "MEDIA_THUMBNAIL_PATH"},
44     {"description", "MEDIA_DESCRIPTION"},
45     {"rating", "MEDIA_RATING"},
46     {"createdDate", "MEDIA_ADDED_TIME"},
47     {"releaseDate", "MEDIA_DATETAKEN"},
48     {"modifiedDate", "MEDIA_MODIFIED_TIME"},
49     {"geolocation.latitude", "MEDIA_LATITUDE"},
50     {"geolocation.longitude", "MEDIA_LONGITUDE"},
51     {"duration", "MEDIA_DURATION"},
52     {"album", "MEDIA_ALBUM"},
53     {"artists", "MEDIA_ARTIST"},
54     {"width", "MEDIA_WIDTH"},
55     {"height", "MEDIA_HEIGHT"},
56     {"genres", "MEDIA_GENRE"},
57     {"size", "MEDIA_SIZE"},
58 };
59
60 std::string escapeValueString(const std::string& data) {
61   ScopeLogger();
62   std::string out;
63   // If string won't be resized, then it will be faster
64   out.reserve(data.size());
65   for (auto c : data) {
66     if (c == '\\')
67       out += "\\\\";
68     else if (c == '\"')
69       out += "\\\"";
70     else if (c == '\'')
71       out += "\\\'";
72     else if (c == '\n')
73       out += "\\\n";
74     else if (c == '\r')
75       out += "\\\r";
76     else
77       out += c;
78   }
79   return out;
80 }
81
82 }  // namespace
83
84 PlatformResult ContentFilter::MapField(const std::string& name, std::string* result) {
85   ScopeLogger();
86   auto it = attributeNameMap.find(name);
87   if (it != attributeNameMap.end()) {
88     if (name == "rating" || name == "description") {
89       std::string warning = "Filtering by attribute '" + name + "'";
90       common::tools::PrintDeprecationWarningFor(warning.c_str());
91     }
92     *result = it->second;
93   } else {
94     return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR);
95   }
96   return PlatformResult(ErrorCode::NO_ERROR);
97 }
98
99 PlatformResult ContentFilter::BuildQuery(const picojson::object& jsFilter,
100                                          std::string* queryToCall) {
101   ScopeLogger();
102   std::vector<std::vector<std::string>> partialqueries;
103   partialqueries.push_back(std::vector<std::string>());
104
105   visitor.SetOnAttributeFilter([&](const std::string& name, AttributeMatchFlag match_flag,
106                                    const picojson::value& match_value) {
107     ScopeLogger("Entered into asynchronous function, visitor.SetOnAttributeFilter's argument");
108
109     std::string query;
110     std::string matchValue;
111
112     PlatformResult result = MapField(name, &query);
113     if (!result) return result;
114
115     if (AttributeMatchFlag::kExactly == match_flag ||
116         AttributeMatchFlag::kFullString == match_flag) {
117       query += " = ";
118     } else if (AttributeMatchFlag::kContains == match_flag ||
119                AttributeMatchFlag::kStartsWith == match_flag ||
120                AttributeMatchFlag::kEndsWith == match_flag) {
121       query += " LIKE ";
122     } else if (AttributeMatchFlag::kExists == match_flag) {
123       query += " IS NOT NULL ";
124     } else {
125       return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR);
126     }
127     if (AttributeMatchFlag::kExists != match_flag) {
128       query.append("\"");
129       matchValue = escapeValueString(JsonCast<std::string>(match_value));
130       if (name == "type") {
131         if (matchValue == "IMAGE") {
132           matchValue = "0";
133         } else if (matchValue == "VIDEO") {
134           matchValue = "1";
135         } else if (matchValue == "AUDIO") {
136           matchValue = "3";
137         } else {  // OTHER
138           matchValue = "4";
139         }
140       } else if (name == "contentURI") {
141         const char* uri_prefix = "file://";
142         size_t found = matchValue.find(uri_prefix);
143         if (found != std::string::npos) {
144           // simple convertion of URI to globalpath
145           matchValue = matchValue.substr(found + strlen(uri_prefix));
146         }
147       }
148       switch (match_flag) {
149         case AttributeMatchFlag::kStartsWith:
150           query += matchValue + "%";
151           break;
152         case AttributeMatchFlag::kEndsWith:
153           query += "%" + matchValue;
154           break;
155         case AttributeMatchFlag::kContains:
156           query += "%" + matchValue + "%";
157           break;
158         default:
159           query += matchValue;
160       }
161       query.append("\"");
162     }
163
164     partialqueries.back().push_back(query);
165
166     return result;
167   });
168
169   visitor.SetOnCompositeFilterBegin([&](CompositeFilterType type) {
170     ScopeLogger("Entered into asynchronous function, visitor.SetOnCompositeFilterBegin's argument");
171     partialqueries.push_back(std::vector<std::string>());
172     return PlatformResult(ErrorCode::NO_ERROR);
173   });
174
175   visitor.SetOnCompositeFilterEnd([&](CompositeFilterType calType) {
176     ScopeLogger("Entered into asynchronous function, visitor.SetOnCompositeFilterEnd's argument");
177     std::string finalQuery;
178     std::string separator;
179
180     if (CompositeFilterType::kUnion == calType)
181       separator = " OR ";
182     else
183       separator = " AND ";
184
185     if (partialqueries.back().empty()) {
186       partialqueries.pop_back();
187       return PlatformResult(ErrorCode::NO_ERROR);
188     }
189     if (partialqueries.back().size() != 1) finalQuery.append("(");
190
191     for (unsigned long i = 0; i < partialqueries.back().size(); i++) {
192       finalQuery += partialqueries.back().at(i);
193       if (i != partialqueries.back().size() - 1) {
194         finalQuery += separator;
195       }
196     }
197
198     if (partialqueries.back().size() != 1) finalQuery.append(")");
199     partialqueries.pop_back();
200     partialqueries.back().push_back(finalQuery);
201
202     return PlatformResult(ErrorCode::NO_ERROR);
203   });
204
205   visitor.SetOnAttributeRangeFilter([&](const std::string& name,
206                                         const picojson::value& initial_value,
207                                         const picojson::value& end_value) {
208     ScopeLogger("Entered into asynchronous function, visitor.SetOnAttributeRangeFilter's argument");
209
210     std::string query = "";
211     std::string paramName;
212     PlatformResult result = MapField(name, &paramName);
213     if (!result) return result;
214
215     std::string initialValue = escapeValueString(JsonCast<std::string>(initial_value));
216     std::string endValue = escapeValueString(JsonCast<std::string>(end_value));
217     query += paramName;
218     query += " >= \"";
219     query += initialValue;
220     query += "\" AND ";
221     query += paramName;
222     query += " <= \"";
223     query += endValue;
224     query += "\"";
225     partialqueries.back().push_back(query);
226
227     return result;
228   });
229
230   if (!visitor.Visit(jsFilter)) {
231     return LogAndCreateResult(ErrorCode::INVALID_VALUES_ERR);
232   }
233
234   if (partialqueries.empty()) {
235     return LogAndCreateResult(ErrorCode::SYNTAX_ERR);
236   }
237   if (partialqueries.back().empty()) {
238     LoggerD("Resolved to empty string!");
239     *queryToCall = "";
240     return PlatformResult(ErrorCode::NO_ERROR);
241   }
242
243   *queryToCall = partialqueries.back().front();
244   return PlatformResult(ErrorCode::NO_ERROR);
245 }
246
247 }  // namespace content
248 }  // namespace extension