From: Pawel Andruszkiewicz Date: Mon, 18 May 2015 13:43:25 +0000 (+0200) Subject: [Contact] Ported AddressBook.find() code from wrt-plugins-tizen. X-Git-Tag: submit/tizen/20151026.073646^2^2~59^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=5c5aec7aead40ce114f6a3a2474ebd72417938ed;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Contact] Ported AddressBook.find() code from wrt-plugins-tizen. [Verification] Pass rate did not change. Change-Id: I55538857e7ff704314492a11592409beae84365b Signed-off-by: Pawel Andruszkiewicz --- diff --git a/src/contact/addressbook.cc b/src/contact/addressbook.cc index 461b5279..4072cb1d 100755 --- a/src/contact/addressbook.cc +++ b/src/contact/addressbook.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ #include #include "contact/contact_instance.h" +#include "contact/contact_search_engine.h" namespace extension { namespace contact { @@ -329,84 +330,30 @@ PlatformResult AddressBookBatchFunc(NativeFunction impl, PlatformResult AddressBookFind(const JsonObject& args, JsonArray& array) { LoggerD("Entered"); PlatformResult status = ContactUtil::CheckDBConnection(); - if (status.IsError()) return status; + if (!status) return status; - // TODO implement contact filter and sorting. - const JsonObject& address_book = FromJson(args, "addressBook"); - long addressbook_id = common::stol(FromJson(address_book, "id")); - int error_code; + long address_book_id = common::stol(FromJson(args, "addressBookId")); - contacts_list_h list = nullptr; + LoggerD("Searching in address book: %d", address_book_id); - if (IsUnified(addressbook_id)) { - LoggerD("calling contacts_db_get_all_records"); - error_code = contacts_db_get_all_records(_contacts_contact._uri, 0, 0, &list); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_db_get_all_records"); - if (status.IsError()) { - LoggerD("contacts_db_get_all_records - exit with error"); - return status; - } - } else { - contacts_query_h query = nullptr; - contacts_filter_h filter = nullptr; - - error_code = contacts_query_create(_contacts_contact._uri, &query); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_query_create"); - if (status.IsError()) return status; - ContactUtil::ContactsQueryHPtr query_ptr(&query, - ContactUtil::ContactsQueryDeleter); - error_code = contacts_filter_create(_contacts_contact._uri, &filter); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_filter_create"); - if (status.IsError()) return status; - ContactUtil::ContactsFilterPtr filter_ptr(filter, - ContactUtil::ContactsFilterDeleter); - error_code = - contacts_filter_add_int(filter, _contacts_contact.address_book_id, - CONTACTS_MATCH_EQUAL, addressbook_id); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_filter_add_int"); - if (status.IsError()) return status; - error_code = contacts_query_set_filter(query, filter); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_query_set_filter"); - if (status.IsError()) return status; - error_code = contacts_db_get_records_with_query(query, 0, 0, &list); - status = ContactUtil::ErrorChecker( - error_code, "Failed contacts_db_get_records_with_query"); - if (status.IsError()) return status; + ContactSearchEngine engine; + if (!IsUnified(address_book_id)) { + engine.SetAddressBookId(address_book_id); } - ContactUtil::ContactsListHPtr list_ptr(&list, - ContactUtil::ContactsListDeleter); - int record_count = 0; - error_code = contacts_list_get_count(list, &record_count); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_list_get_count"); - if (status.IsError()) return status; - - contacts_list_first(list); - for (int i = 0; i < record_count; i++) { - contacts_record_h record; - error_code = contacts_list_get_current_record_p(list, &record); - status = ContactUtil::ErrorChecker( - error_code, "Failed contacts_list_get_current_record_p"); - if (status.IsError()) return status; - - int id_value = 0; - error_code = - contacts_record_get_int(record, _contacts_contact.id, &id_value); - status = - ContactUtil::ErrorChecker(error_code, "Failed contacts_record_get_int"); - if (status.IsError()) return status; + const auto filter_it = args.find("filter"); + if (args.end() != filter_it && filter_it->second.is()) { + status = engine.ApplyFilter(filter_it->second); + if (!status) return status; + } - array.push_back(JsonValue(static_cast(id_value))); - contacts_list_next(list); + const auto sort_mode_it = args.find("sortMode"); + if (args.end() != sort_mode_it && sort_mode_it->second.is()) { + status = engine.SetSortMode(sort_mode_it->second); + if (!status) return status; } - return PlatformResult(ErrorCode::NO_ERROR); + return engine.Find(&array); } PlatformResult AddressBookAddGroup(const JsonObject& args, JsonObject& out) { diff --git a/src/contact/contact.gyp b/src/contact/contact.gyp index 1d0f5f92..9576bf04 100644 --- a/src/contact/contact.gyp +++ b/src/contact/contact.gyp @@ -19,6 +19,8 @@ 'addressbook.h', 'contact_manager.cc', 'contact_manager.h', + 'contact_search_engine.cc', + 'contact_search_engine.h', 'contact_util.cc', 'contact_util.h', 'person.cc', diff --git a/src/contact/contact_search_engine.cc b/src/contact/contact_search_engine.cc new file mode 100644 index 00000000..d084e2f8 --- /dev/null +++ b/src/contact/contact_search_engine.cc @@ -0,0 +1,1440 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "contact/contact_search_engine.h" + +#include + +#include "common/converter.h" +#include "common/logger.h" + +#include "contact/contact_util.h" + +namespace extension { +namespace contact { + +using common::ErrorCode; +using common::IsNull; +using common::JsonCast; +using common::PlatformResult; + +namespace { + +std::string ToString(const picojson::value& value) { + if (value.is()) { + return value.get(); + } else { + return ""; + } +} + +std::tm* ToDateTm(const picojson::value& value) { + static std::tm t; + memset(&t, 0, sizeof(std::tm)); + strptime(ToString(value).c_str(), "%Y-%m-%dT%H:%M:%S.%zZ", &t); + return &t; +} + +std::string ToDateDbStr(const tm& date) { + std::stringstream ss; + ss << std::setfill('0') << std::setiosflags(std::ios::right) << std::setw(4) + << (date.tm_year + 1900); + ss << std::setfill('0') << std::setiosflags(std::ios::right) << std::setw(2) + << (date.tm_mon + 1); + ss << std::setfill('0') << std::setiosflags(std::ios::right) << std::setw(2) + << date.tm_mday; + + return ss.str(); +} + +int StrToInt(const std::string& str) { + int result = 0; + + std::istringstream iss(str); + iss >> result; + + return result; +} + +int ToDateDbInt(const tm& date) { + return StrToInt(ToDateDbStr(date)); +} + +} // namespace + +ContactSearchEngine::PropertiesMap ContactSearchEngine::s_properties_map_ = { + {"id", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.id, PrimitiveType_Long } }, + {"personId", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.person_id, PrimitiveType_Long } }, + {"addressBookId", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.address_book_id, PrimitiveType_Long } }, + {"lastUpdated", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.changed_time, PrimitiveType_Long } }, + {"isFavorite", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.is_favorite, PrimitiveType_Boolean } }, + {"name.prefix", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.prefix, PrimitiveType_String } }, + {"name.suffix", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.suffix, PrimitiveType_String } }, + {"name.firstName", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.first, PrimitiveType_String } }, + {"name.middleName", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.addition, PrimitiveType_String } }, + {"name.lastName", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.last, PrimitiveType_String } }, + {"name.nicknames", { _contacts_nickname._uri, _contacts_nickname.contact_id, _contacts_nickname.name, PrimitiveType_String } }, + {"name.phoneticFirstName", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.phonetic_first, PrimitiveType_String } }, + {"name.phoneticMiddleName", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.phonetic_middle, PrimitiveType_String } }, + {"name.phoneticLastName", { _contacts_name._uri, _contacts_name.contact_id, _contacts_name.phonetic_last, PrimitiveType_String } }, + {"name.displayName", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.display_name, PrimitiveType_String } }, + {"addresses.country", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.country, PrimitiveType_String } }, + {"addresses.region", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.region, PrimitiveType_String } }, + {"addresses.city", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.locality, PrimitiveType_String } }, + {"addresses.streetAddress", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.street, PrimitiveType_String } }, + {"addresses.additionalInformation", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.extended, PrimitiveType_String } }, + {"addresses.postalCode", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.postal_code, PrimitiveType_String } }, + {"addresses.isDefault", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.is_default, PrimitiveType_Boolean } }, + {"addresses.types", { _contacts_address._uri, _contacts_address.contact_id, _contacts_address.type, PrimitiveType_Long } }, + {"photoURI", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.image_thumbnail_path, PrimitiveType_String } }, + {"phoneNumbers.number", { _contacts_number._uri, _contacts_number.contact_id, _contacts_number.number, PrimitiveType_String } }, + {"phoneNumbers.isDefault", { _contacts_number._uri, _contacts_number.contact_id, _contacts_number.is_default, PrimitiveType_Boolean } }, + {"phoneNumbers.types", { _contacts_number._uri, _contacts_number.contact_id, _contacts_number.type, PrimitiveType_Long } }, + {"emails.email", { _contacts_email._uri, _contacts_email.contact_id, _contacts_email.email, PrimitiveType_String } }, + {"emails.isDefault", { _contacts_email._uri, _contacts_email.contact_id, _contacts_email.is_default, PrimitiveType_Boolean } }, + {"emails.types", { _contacts_email._uri, _contacts_email.contact_id, _contacts_email.type, PrimitiveType_Long } }, + {"birthday", { _contacts_event._uri, _contacts_event.contact_id, _contacts_event.date, PrimitiveType_Long } }, + {"anniversaries.date", { _contacts_event._uri, _contacts_event.contact_id, _contacts_event.date, PrimitiveType_Long } }, + {"anniversaries.label", { _contacts_event._uri, _contacts_event.contact_id, _contacts_event.label, PrimitiveType_String } }, + {"organizations.name", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.name, PrimitiveType_String } }, + {"organizations.department",{ _contacts_company._uri, _contacts_company.contact_id, _contacts_company.department, PrimitiveType_String } }, + {"organizations.title", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.job_title, PrimitiveType_String } }, + {"organizations.role", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.role, PrimitiveType_String } }, + {"organizations.logoURI", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.logo, PrimitiveType_String } }, + {"organizations.assistant", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.assistant_name, PrimitiveType_String } }, + {"organizations.location", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.location, PrimitiveType_String } }, + {"organizations.description", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.description, PrimitiveType_String } }, + {"organizations.phoneticName", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.phonetic_name, PrimitiveType_String } }, + {"organizations.type", { _contacts_company._uri, _contacts_company.contact_id, _contacts_company.type, PrimitiveType_Long } }, + {"notes", { _contacts_note._uri, _contacts_note.contact_id, _contacts_note.note, PrimitiveType_String } }, + {"urls.url", { _contacts_url._uri, _contacts_url.contact_id, _contacts_url.url, PrimitiveType_String } }, + {"urls.type", { _contacts_url._uri, _contacts_url.contact_id, _contacts_url.type, PrimitiveType_Long } }, + {"ringtoneURI", { _contacts_simple_contact._uri, _contacts_simple_contact.id, _contacts_simple_contact.ringtone_path, PrimitiveType_String } }, + {"groupIds", { _contacts_group_relation._uri, _contacts_group_relation.contact_id, _contacts_group_relation.group_id, PrimitiveType_Long } } +}; + +// implementation ported from wrt-plugins-tizen +// TODO: instead of executing multiple queries and combining the results, +// create multiple filters, combine them into one, add sorting and +// execute a single query + +ContactSearchEngine::ContactSearchEngine() + : addressbook_id_(0), + is_addressbook_id_is_set_(false), + is_filter_set_(false), + is_sort_mode_set_(false), + is_sort_mode_asc_(false) { + LoggerD("Entered"); +} + +void ContactSearchEngine::SetAddressBookId(long id) { + LoggerD("Entered"); + addressbook_id_ = id; + is_addressbook_id_is_set_ = true; +} + +common::PlatformResult ContactSearchEngine::ApplyFilter(const picojson::value& filter, int depth) { + LoggerD("Entered"); + + if (!filter.is()) { + return PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Failed to set filter"); + } + + common::FilterVisitor visitor; + + visitor.SetOnAttributeFilter([&depth, this](const std::string& name, + common::AttributeMatchFlag match_flag, + const picojson::value& match_value) { + return ApplyAttributeFilter(name, match_flag, match_value, depth); + }); + + visitor.SetOnAttributeRangeFilter([&depth, this](const std::string& name, + const picojson::value& initial_value, + const picojson::value& end_value) { + return ApplyAttributeRangeFilter(name, initial_value, end_value, depth); + }); + + visitor.SetOnCompositeFilterBegin([&depth, this](common::CompositeFilterType type) { + LongSetPtrVectorPtr id_sets = LongSetPtrVectorPtr(new LongSetPtrVector()); + contact_id_set_array_stack_.push(id_sets); + ++depth; + + return PlatformResult(ErrorCode::NO_ERROR); + }); + + visitor.SetOnCompositeFilterEnd([&depth, this](common::CompositeFilterType type) { + --depth; + auto id_sets = contact_id_set_array_stack_.top(); + contact_id_set_array_stack_.pop(); + + LongSetPtr new_id_set = LongSetPtr(new LongSet()); + if (common::CompositeFilterType::kUnion == type) { + GetUnion(id_sets, new_id_set); + } else if (common::CompositeFilterType::kIntersection == type) { + GetIntersection(id_sets, new_id_set); + } + + if (!depth) { + filtered_contact_ids_ = new_id_set; + } else if (new_id_set) { + contact_id_set_array_stack_.top()->push_back(new_id_set); + } + + return PlatformResult(ErrorCode::NO_ERROR); + }); + + auto status = visitor.Visit(filter.get()); + if (!status) return status; + + if (filtered_contact_ids_) { + is_filter_set_ = true; + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult ContactSearchEngine::SetSortMode(const picojson::value& sort_mode) { + LoggerD("Entered"); + + if (!sort_mode.is()) { + return PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Failed to set sort mode"); + } + + std::string attribute = sort_mode.get("attributeName").to_str(); + + const auto iter = s_properties_map_.find(attribute); + if (s_properties_map_.end() == iter) { + std::string msg = "SortMode doesn't support attribute: " + attribute; + LoggerE("%s", msg.c_str()); + return PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg); + } + + is_sort_mode_set_ = true; + sort_mode_attribute_ = attribute; + is_sort_mode_asc_ = sort_mode.get("attributeName").to_str() == "ASC"; + + return PlatformResult(ErrorCode::NO_ERROR); +} + +common::PlatformResult ContactSearchEngine::Find(picojson::array* out) { + LoggerD("Entered"); + + if (is_filter_set_) { + if (is_sort_mode_set_) { + LongVectorPtr ids = LongVectorPtr(new LongVector()); + + const auto iter = s_properties_map_.find(sort_mode_attribute_); + const FilterPropertyStruct& property = iter->second; + + if (filtered_contact_ids_->size()) { + SortContacts(property, ids, is_sort_mode_asc_, filtered_contact_ids_); + return GetContacts(ids->begin(), ids->end(), out); + } else { + return PlatformResult(ErrorCode::NO_ERROR); + } + } else { + if (filtered_contact_ids_->size()) { + return GetContacts(filtered_contact_ids_->begin(), filtered_contact_ids_->end(), out); + } else { + return PlatformResult(ErrorCode::NO_ERROR); + } + } + } else { + if (is_sort_mode_set_) { + const auto iter = s_properties_map_.find(sort_mode_attribute_); + const FilterPropertyStruct& property = iter->second; + + return GetAllContactsSorted(property, is_sort_mode_asc_, out); + } else { + return GetAllContacts(out); + } + } +} + +PlatformResult ContactSearchEngine::ApplyAttributeFilter( + const std::string& name, common::AttributeMatchFlag match_flag, + const picojson::value& match_value, int depth) { + LoggerD("Entered"); + + LongSetPtr id_set = LongSetPtr(new LongSet()); + + if ("id" == name) { + if (common::AttributeMatchFlag::kExists != match_flag) { + id_set->insert(static_cast(JsonCast(match_value))); + + if (!depth) { + filtered_contact_ids_ = id_set; + } else { + contact_id_set_array_stack_.top()->push_back(id_set); + } + } + return PlatformResult(ErrorCode::NO_ERROR); + } else if ("addresses.types" == name || + "emails.types" == name || + "phoneNumbers.types" == name || + "urls.type" == name) { + if (!depth) { + filtered_contact_ids_ = LongSetPtr(); + } + return PlatformResult(ErrorCode::NO_ERROR); + } + + const auto iter = s_properties_map_.find(name); + if (s_properties_map_.end() == iter) { + std::string msg = "Unknown attribute name: " + name; + LoggerE("%s", msg.c_str()); + return PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg); + } + + const auto& properties = iter->second; + + if (PrimitiveType_Boolean == properties.type) { + bool value = true; + if (common::AttributeMatchFlag::kExists != match_flag) { + value = JsonCast(match_value); + } + + auto status = QueryAttributeBool(properties, id_set, value); + if (!status) return status; + } else if (PrimitiveType_String == properties.type) { + std::string value = ""; + + if (common::AttributeMatchFlag::kExists != match_flag) { + if ("photoURI" == name || + "ringtoneURI" == name || + "organizations.logoURI" == name) { + value = ContactUtil::ConvertUriToPath(JsonCast(match_value)); + } else { + value = JsonCast(match_value); + } + } + + contacts_match_str_flag_e flag = CONTACTS_MATCH_EXISTS; + + if (common::AttributeMatchFlag::kExactly == match_flag) { + flag = CONTACTS_MATCH_EXACTLY; + } else if (common::AttributeMatchFlag::kFullString == match_flag) { + flag = CONTACTS_MATCH_FULLSTRING; + } else if (common::AttributeMatchFlag::kContains == match_flag) { + flag = CONTACTS_MATCH_CONTAINS; + } else if (common::AttributeMatchFlag::kStartsWith == match_flag) { + flag = CONTACTS_MATCH_STARTSWITH; + } else if (common::AttributeMatchFlag::kEndsWith == match_flag) { + flag = CONTACTS_MATCH_ENDSWITH; + } else if (common::AttributeMatchFlag::kExists == match_flag) { + flag = CONTACTS_MATCH_EXISTS; + } + + auto status = QueryAttributeString(properties, id_set, flag, value.c_str()); + if (!status) return status; + } else if (PrimitiveType_Long == properties.type) { + int value = 0; + + if (common::AttributeMatchFlag::kExists != match_flag) { + if ("lastUpdated" == name) { + value = static_cast(mktime(ToDateTm(match_value))); + } else if ("birthday" == name || "anniversaries.date" == name) { + value = ToDateDbInt(*ToDateTm(match_value)); + } else { + value = static_cast(JsonCast(match_value)); + } + } + + contacts_match_int_flag_e flag; + if (common::AttributeMatchFlag::kExists == match_flag) { + flag = CONTACTS_MATCH_GREATER_THAN_OR_EQUAL; + value = 0; + } else if (common::AttributeMatchFlag::kStartsWith == match_flag || + common::AttributeMatchFlag::kContains == match_flag) { + flag = CONTACTS_MATCH_GREATER_THAN_OR_EQUAL; + } else if (common::AttributeMatchFlag::kEndsWith == match_flag) { + flag = CONTACTS_MATCH_LESS_THAN_OR_EQUAL; + } else { + flag = CONTACTS_MATCH_EQUAL; + } + + if ("birthday" == name || "anniversaries.date" == name) { + auto status = QueryAttributeDate(name, properties, id_set, flag, value); + if (!status) return status; + } else { + auto status = QueryAttributeInt(properties, id_set, flag, value); + if (!status) return status; + } + } + + if (!depth) { + filtered_contact_ids_ = id_set; + } else { + if (id_set) { + contact_id_set_array_stack_.top()->push_back(id_set); + } + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult ContactSearchEngine::ApplyAttributeRangeFilter( + const std::string& name, const picojson::value& initial_value, + const picojson::value& end_value, int depth) { + LoggerD("Entered"); + + bool initial_value_set = (!IsNull(initial_value)); + bool end_value_set = (!IsNull(end_value)); + + if (!initial_value_set && !end_value_set) { + if (!depth) { + filtered_contact_ids_ = LongSetPtr(); + } + return PlatformResult(ErrorCode::NO_ERROR); + } + + if ("addresses.types" == name || + "emails.types" == name || + "phoneNumbers.types" == name || + "urls.type" == name) { + if (!depth) { + filtered_contact_ids_ = LongSetPtr(); + } + return PlatformResult(ErrorCode::NO_ERROR); + } + + const auto iter = s_properties_map_.find(name); + if (s_properties_map_.end() == iter) { + std::string msg = "Unknown attribute name: " + name; + LoggerE("%s", msg.c_str()); + return PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg); + } + + LongSetPtr id_set = LongSetPtr(new LongSet()); + const FilterPropertyStruct& properties = iter->second; + + if (PrimitiveType_Boolean == properties.type) { + bool initial_value_bool = false; + bool end_value_bool = false; + + if (initial_value_set) { + initial_value_bool = JsonCast(initial_value); + } + if (end_value_set) { + end_value_bool = JsonCast(end_value); + } + + auto status = QueryAttributeRangeBool(properties, id_set, initial_value_set, + initial_value_bool, end_value_set, + end_value_bool); + if (!status) return status; + } else if (PrimitiveType_String == properties.type) { + const char* initial_value_str = nullptr; + const char* end_value_str = nullptr; + + if (initial_value_set) { + initial_value_str = JsonCast(initial_value).c_str(); + } + if (end_value_set) { + end_value_str = JsonCast(end_value).c_str(); + } + + auto status = QueryAttributeRangeString(properties, id_set, + initial_value_str, end_value_str); + if (!status) return status; + } else if (PrimitiveType_Long == properties.type) { + int initial_value_int = 0; + int end_value_int = 0; + + if ("lastUpdated" == name) { + if (initial_value_set) { + initial_value_int = static_cast(mktime(ToDateTm(initial_value))); + } + if (end_value_set) { + end_value_int = static_cast(mktime(ToDateTm(end_value))); + } + } else if ("birthday" == name || "anniversaries.date" == name) { + if (initial_value_set) { + initial_value_int = ToDateDbInt(*ToDateTm(initial_value)); + } + if (end_value_set) { + end_value_int = ToDateDbInt(*ToDateTm(end_value)); + } + } else { + if (initial_value_set) { + initial_value_int = static_cast(JsonCast(initial_value)); + } + if (end_value_set) { + end_value_int = static_cast(JsonCast(end_value)); + } + } + + if ("birthday" == name || "anniversaries.date" == name) { + auto status = QueryAttributeRangeDate(name, properties, id_set, + initial_value_set, + initial_value_int, end_value_set, + end_value_int); + if (!status) return status; + } else { + auto status = QueryAttributeRangeInt(properties, id_set, + initial_value_set, initial_value_int, + end_value_set, end_value_int); + if (!status) return status; + } + } + + if (!depth) { + filtered_contact_ids_ = id_set; + } else { + contact_id_set_array_stack_.top()->push_back(id_set); + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult ContactSearchEngine::GetAllContactsSorted( + const FilterPropertyStruct& attribute_properties, bool is_ascending, + picojson::array* out) { + LoggerD("Entered"); + + LongVectorPtr sorted_ids = LongVectorPtr(new LongVector()); + SortContacts(attribute_properties, sorted_ids, is_ascending); + + return GetContacts(sorted_ids->begin(), sorted_ids->end(), out); +} + +PlatformResult ContactSearchEngine::GetAllContacts(picojson::array* out) { + LoggerD("Entered"); + + contacts_list_h list = nullptr; + int error_code = 0; + PlatformResult status(ErrorCode::NO_ERROR); + + if (!is_addressbook_id_is_set_) { + error_code = contacts_db_get_all_records(_contacts_contact._uri, 0, 0, + &list); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_db_get_all_records"); + if (!status) return status; + } else { + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + error_code = contacts_query_create(_contacts_contact._uri, &query); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(_contacts_contact._uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + error_code = contacts_filter_add_int(filter, + _contacts_contact.address_book_id, + CONTACTS_MATCH_EQUAL, + addressbook_id_); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + + error_code = contacts_query_set_filter(query, filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_set_filter"); + if (!status) return status; + + error_code = contacts_db_get_records_with_query(query, 0, 0, &list); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_db_get_records_with_query"); + if (!status) return status; + } + + ContactUtil::ContactsListHPtr list_ptr(&list, + ContactUtil::ContactsListDeleter); + + int record_count = 0; + error_code = contacts_list_get_count(list, &record_count); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_list_get_count"); + if (!status) return status; + + contacts_list_first(list); + for (int i = 0; i < record_count; ++i) { + contacts_record_h record = nullptr; + + error_code = contacts_list_get_current_record_p(list, &record); + if (CONTACTS_ERROR_NONE != error_code || !record) { + LoggerW("contacts_list_get_current_record_p() failed: %d", error_code); + continue; + } + + int id_value = 0; + error_code = contacts_record_get_int(record, _contacts_contact.id, &id_value); + status = ContactUtil::ErrorChecker(error_code, "Failed contacts_record_get_int"); + if (!status) return status; + + out->push_back(picojson::value(static_cast(id_value))); + + error_code = contacts_list_next(list); + + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_list_next() failed: %d", error_code); + } + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +template +PlatformResult ContactSearchEngine::GetContacts(Iterator begin, Iterator end, + picojson::array* out) { + LoggerD("Entered"); + + for (auto iter = begin; iter != end; ++iter) { + const auto id = *iter; + + if (is_addressbook_id_is_set_) { + contacts_record_h record = nullptr; + int error_code = contacts_db_get_record(_contacts_contact._uri, id, &record); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerE("Failed to get contact with ID: %d", id); + continue; + } + + ContactUtil::ContactsRecordHPtr record_ptr(&record, + ContactUtil::ContactsDeleter); + int address_book_id = 0; + + error_code = contacts_record_get_int(record, + _contacts_contact.address_book_id, + &address_book_id); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerE("Failed to get address book ID of contact with ID: %d", id); + continue; + } + + if (address_book_id != addressbook_id_) { + LoggerE("Wrong address book"); + continue; + } + } + + out->push_back(picojson::value(static_cast(id))); + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +common::PlatformResult ContactSearchEngine::GetQueryResults( + contacts_query_h query, contacts_filter_h filter, unsigned int property_id, + LongSetPtr result) { + LoggerD("Entered"); + + int error_code = contacts_query_set_filter(query, filter); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_set_filter"); + if (!status) return status; + + contacts_list_h list = nullptr; + error_code = contacts_db_get_records_with_query(query, 0, 0, &list); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_db_get_records_with_query"); + if (!status) return status; + + ContactUtil::ContactsListHPtr list_ptr(&list, + ContactUtil::ContactsListDeleter); + + int record_count = 0; + error_code = contacts_list_get_count(list, &record_count); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_list_get_count"); + if (!status) return status; + + contacts_list_first(*list_ptr); + for (int i = 0; i < record_count; ++i) { + contacts_record_h record = nullptr; + error_code = contacts_list_get_current_record_p(list, &record); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_list_get_current_record_p"); + if (!status) return status; + + int value = 0; + error_code = contacts_record_get_int(record, property_id, &value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_record_get_int"); + if (!status) return status; + + result->insert(value); + error_code = contacts_list_next(*list_ptr); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("Failed to get next contact list: %d", error_code); + } + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult ContactSearchEngine::QueryAttributeBool( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool match_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + error_code = contacts_filter_add_bool(filter, property_id, match_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_bool"); + if (!status) return status; + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeInt( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + contacts_match_int_flag_e match, int match_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + error_code = contacts_filter_add_int(filter, property_id, match, match_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeString( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + contacts_match_str_flag_e match, const char* match_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + if (_contacts_number.number == property_id + && CONTACTS_MATCH_CONTAINS == match) { + property_id = _contacts_number.normalized_number; + } + + error_code = contacts_filter_add_str(filter, property_id, match, match_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_str"); + if (!status) return status; + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeDate( + const std::string& attr_name, + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + contacts_match_int_flag_e match, int match_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + error_code = contacts_filter_add_int(filter, property_id, match, + match_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + + if ("birthday" == attr_name) { + error_code = contacts_filter_add_operator(filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(filter, _contacts_event.type, + CONTACTS_MATCH_EQUAL, + CONTACTS_EVENT_TYPE_BIRTH); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } else if ("anniversaries.date" == attr_name) { + error_code = contacts_filter_add_operator(filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(filter, _contacts_event.type, + CONTACTS_MATCH_EQUAL, + CONTACTS_EVENT_TYPE_ANNIVERSARY); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + } + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeRangeBool( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool initial_value_is_set, + bool initial_value, bool end_value_is_set, + bool end_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + if (initial_value_is_set && end_value_is_set) { + if (initial_value == end_value) { + if (initial_value) { + error_code = contacts_filter_add_bool(filter, property_id, true); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_bool"); + if (!status) return status; + } else if (!end_value) { + error_code = contacts_filter_add_bool(filter, property_id, false); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_bool"); + if (!status) return status; + } + } + } else if (initial_value_is_set) { + if (initial_value) { + error_code = contacts_filter_add_bool(filter, property_id, true); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_bool"); + if (!status) return status; + } + } else if (end_value_is_set) { + if (!end_value) { + error_code = contacts_filter_add_bool(filter, property_id, false); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_bool"); + if (!status) return status; + } + } + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeRangeInt( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool initial_value_is_set, + int initial_value, bool end_value_is_set, int end_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + if (initial_value_is_set && end_value_is_set) { + contacts_filter_h sub_filter = nullptr; + + error_code = contacts_filter_create(view_uri, &sub_filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr sub_filter_ptr(sub_filter, + ContactUtil::ContactsFilterDeleter); + + error_code = contacts_filter_add_int(sub_filter, property_id, + CONTACTS_MATCH_GREATER_THAN_OR_EQUAL, + initial_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + + error_code = contacts_filter_add_operator(sub_filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(sub_filter, property_id, + CONTACTS_MATCH_LESS_THAN_OR_EQUAL, + end_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + + error_code = contacts_filter_add_filter(filter, sub_filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_filter"); + if (!status) return status; + } else if (initial_value_is_set) { + error_code = contacts_filter_add_int(filter, property_id, + CONTACTS_MATCH_GREATER_THAN_OR_EQUAL, + initial_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } else if (end_value_is_set) { + error_code = contacts_filter_add_int(filter, property_id, + CONTACTS_MATCH_LESS_THAN_OR_EQUAL, + end_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeRangeString( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + const char* initial_value, const char* end_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + if (initial_value && end_value) { + contacts_filter_h sub_filter = nullptr; + + error_code = contacts_filter_create(view_uri, &sub_filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr sub_filter_ptr(sub_filter, + ContactUtil::ContactsFilterDeleter); + + // TODO To be supported: start + error_code = contacts_filter_add_str(sub_filter, property_id, + CONTACTS_MATCH_STARTSWITH, + initial_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_str"); + if (!status) return status; + + error_code = contacts_filter_add_operator(sub_filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_str(sub_filter, property_id, + CONTACTS_MATCH_ENDSWITH, end_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_str"); + if (!status) return status; + + error_code = contacts_filter_add_filter(filter, sub_filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_filter"); + if (!status) return status; + } else if (initial_value) { + error_code = contacts_filter_add_str(filter, property_id, + CONTACTS_MATCH_STARTSWITH, + initial_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_str"); + if (!status) return status; + } else if (end_value) { + error_code = contacts_filter_add_str(filter, property_id, + CONTACTS_MATCH_ENDSWITH, end_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_str"); + if (!status) return status; + } + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::QueryAttributeRangeDate( + const std::string& attr_name, + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool initial_value_is_set, int initial_value, bool end_value_is_set, + int end_value) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + if (initial_value_is_set && end_value_is_set) { + error_code = contacts_filter_add_int(filter, property_id, + CONTACTS_MATCH_GREATER_THAN_OR_EQUAL, + initial_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + + error_code = contacts_filter_add_operator(filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(filter, property_id, + CONTACTS_MATCH_LESS_THAN_OR_EQUAL, + end_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } else if (initial_value_is_set) { + error_code = contacts_filter_add_int(filter, property_id, + CONTACTS_MATCH_GREATER_THAN_OR_EQUAL, + initial_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } else if (end_value_is_set) { + error_code = contacts_filter_add_int(filter, property_id, + CONTACTS_MATCH_LESS_THAN_OR_EQUAL, + end_value); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } + + if ("birthday" == attr_name) { + error_code = contacts_filter_add_operator(filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(filter, _contacts_event.type, + CONTACTS_MATCH_EQUAL, + CONTACTS_EVENT_TYPE_BIRTH); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } else if ("anniversaries.date" == attr_name) { + error_code = contacts_filter_add_operator(filter, + CONTACTS_FILTER_OPERATOR_AND); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(filter, _contacts_event.type, + CONTACTS_MATCH_EQUAL, + CONTACTS_EVENT_TYPE_ANNIVERSARY); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } + + return GetQueryResults(query, filter, property_contact_id, result); +} + +PlatformResult ContactSearchEngine::SortContacts( + const FilterPropertyStruct& attribute_properties, LongVectorPtr result, + bool is_ascending, LongSetPtr ids) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_filter_h filter = nullptr; + contacts_list_h list = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_filter_create(view_uri, &filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_create"); + if (!status) return status; + + ContactUtil::ContactsFilterPtr filter_ptr(filter, + ContactUtil::ContactsFilterDeleter); + + auto iter = ids->begin(); + if (ids->end() != iter) { + error_code = contacts_filter_add_int(filter, property_contact_id, + CONTACTS_MATCH_EQUAL, *iter); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_filter_add_int() failed: %d", error_code); + } + for (; iter != ids->end(); ++iter) { + error_code = contacts_filter_add_operator(filter, + CONTACTS_FILTER_OPERATOR_OR); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_operator"); + if (!status) return status; + + error_code = contacts_filter_add_int(filter, property_contact_id, + CONTACTS_MATCH_EQUAL, *iter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_filter_add_int"); + if (!status) return status; + } + } + + error_code = contacts_query_set_sort(query, property_id, is_ascending); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_set_sort"); + if (!status) return status; + + error_code = contacts_query_set_filter(query, filter); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_set_filter"); + if (!status) return status; + + error_code = contacts_db_get_records_with_query(query, 0, 0, &list); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_db_get_records_with_query"); + if (!status) return status; + + ContactUtil::ContactsListHPtr list_ptr(&list, + ContactUtil::ContactsListDeleter); + + int record_count = 0; + error_code = contacts_list_get_count(list, &record_count); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_list_get_count"); + if (!status) return status; + + LongSet overlapping_ids; + + contacts_list_first(*list_ptr); + for (int i = 0; i < record_count; ++i) { + contacts_record_h record; + error_code = contacts_list_get_current_record_p(list, &record); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_list_get_current_record_p() failed: %d", error_code); + continue; + } + + int value = 0; + error_code = contacts_record_get_int(record, property_contact_id, &value); + LoggerW("contacts_record_get_int() failed: %d", error_code); + if (CONTACTS_ERROR_NONE != error_code) { + continue; + } + + if (overlapping_ids.find(value) == overlapping_ids.end()) { + result->push_back(value); + overlapping_ids.insert(value); + } + + error_code = contacts_list_next(list); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_list_next() failed: %d", error_code); + } + } + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult ContactSearchEngine::SortContacts( + const FilterPropertyStruct& attribute_properties, LongVectorPtr result, + bool is_ascending) { + LoggerD("Entered"); + + const char* view_uri = attribute_properties.view_uri; + unsigned int property_contact_id = attribute_properties.property_contact_id; + unsigned int property_id = attribute_properties.property_id; + + contacts_query_h query = nullptr; + contacts_list_h list = nullptr; + + int error_code = contacts_query_create(view_uri, &query); + auto status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_create"); + if (!status) return status; + + ContactUtil::ContactsQueryHPtr query_ptr(&query, + ContactUtil::ContactsQueryDeleter); + + error_code = contacts_query_set_sort(query, property_id, is_ascending); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_query_set_sort"); + if (!status) return status; + + error_code = contacts_db_get_records_with_query(query, 0, 0, &list); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_db_get_records_with_query"); + if (!status) return status; + + ContactUtil::ContactsListHPtr list_ptr(&list, + ContactUtil::ContactsListDeleter); + + int record_count = 0; + error_code = contacts_list_get_count(list, &record_count); + status = ContactUtil::ErrorChecker(error_code, + "Failed contacts_list_get_count"); + if (!status) return status; + + LongSet overlapping_ids; + + contacts_list_first(*list_ptr); + for (int i = 0; i < record_count; ++i) { + contacts_record_h record; + error_code = contacts_list_get_current_record_p(list, &record); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_list_get_current_record_p() failed: %d", error_code); + continue; + } + + int value = 0; + error_code = contacts_record_get_int(record, property_contact_id, &value); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_record_get_int() failed: %d", error_code); + continue; + } + + if (overlapping_ids.find(value) == overlapping_ids.end()) { + result->push_back(value); + overlapping_ids.insert(value); + } + + error_code = contacts_list_next(list); + if (CONTACTS_ERROR_NONE != error_code) { + LoggerW("contacts_list_next() failed: %d", error_code); + } + } + return PlatformResult(ErrorCode::NO_ERROR); +} + +void ContactSearchEngine::GetIntersection(LongSetPtrVectorPtr idSets, + LongSetPtr result) { + LongSetPtrVector::iterator i; + + if (idSets->size() == 0) { + result = LongSetPtr(); + return; + } else if (idSets->size() == 1) { + *result = **(idSets->begin()); + return; + } + + LongSetPtrVector::iterator minIter; + LongSetPtrVector::size_type minSize = std::numeric_limits< + LongSetPtrVector::size_type>::max(); + + for (i = idSets->begin(); i != idSets->end(); i++) { + LongSetPtr idSet = *i; + LongSetPtrVector::size_type size = idSet->size(); + if (minSize > size) { + minSize = size; + minIter = i; + } + } + + LongSetPtr p = *minIter; + LongSetPtrVectorPtr sa = LongSetPtrVectorPtr(new LongSetPtrVector()); + for (i = idSets->begin(); i != idSets->end(); i++) { + if (minIter != i) { + sa->push_back(*i); + } + } + + for (auto iter = p->begin(); iter != p->end(); ++iter) { + bool excluded = false; + int value = *iter; + + for (i = sa->begin(); i != sa->end(); i++) { + LongSetPtr id_set = *i; + if (id_set->find(value) == id_set->end()) { + excluded = true; + break; + } + } + + if (!excluded) { + result->insert(value); + } + } +} + +void ContactSearchEngine::GetUnion(LongSetPtrVectorPtr id_sets, + LongSetPtr result) { + if (!id_sets->size()) { + result = LongSetPtr(); + return; + } else if (1 == id_sets->size()) { + *result = **(id_sets->begin()); + return; + } + + for (auto i = id_sets->begin(); i != id_sets->end(); ++i) { + LongSetPtr ids = *i; + for (auto j = ids->begin(); j != ids->end(); ++j) { + result->insert(*j); + } + } +} + +} // namespace contact +} // namespace extension diff --git a/src/contact/contact_search_engine.h b/src/contact/contact_search_engine.h new file mode 100644 index 00000000..7cc91ed4 --- /dev/null +++ b/src/contact/contact_search_engine.h @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONTACT_CONTACT_SEARCH_ENGINE_H_ +#define CONTACT_CONTACT_SEARCH_ENGINE_H_ + +#include +#include +#include +#include + +#include + +#include "common/filter-utils.h" +#include "common/picojson.h" +#include "common/platform_result.h" + +namespace extension { +namespace contact { + +class ContactSearchEngine { + public: + ContactSearchEngine(); + + void SetAddressBookId(long id); + common::PlatformResult ApplyFilter(const picojson::value& filter, int depth = 0); + common::PlatformResult SetSortMode(const picojson::value& sort_mode); + + common::PlatformResult Find(picojson::array* out); + + private: + typedef std::vector LongVector; + typedef std::shared_ptr LongVectorPtr; + + typedef std::set LongSet; + typedef std::shared_ptr LongSetPtr; + + typedef std::vector LongSetPtrVector; + typedef std::shared_ptr LongSetPtrVectorPtr; + + enum PrimitiveType { + PrimitiveType_Boolean, + PrimitiveType_Long, + PrimitiveType_String, + }; + + struct FilterPropertyStruct { + const char* view_uri; + const unsigned int property_contact_id; + const unsigned int property_id; + const PrimitiveType type; + }; + + typedef std::map PropertiesMap; + + common::PlatformResult ApplyAttributeFilter( + const std::string& name, common::AttributeMatchFlag match_flag, + const picojson::value& match_value, int depth); + common::PlatformResult ApplyAttributeRangeFilter( + const std::string& name, const picojson::value& initial_value, + const picojson::value& end_value, int depth); + + common::PlatformResult GetQueryResults(contacts_query_h query, + contacts_filter_h filter, + unsigned int property_id, + LongSetPtr result); + + common::PlatformResult GetAllContactsSorted( + const FilterPropertyStruct& attribute_properties, bool is_ascending, + picojson::array* out); + common::PlatformResult GetAllContacts(picojson::array* out); + template + common::PlatformResult GetContacts(Iterator begin, Iterator end, + picojson::array* out); + + common::PlatformResult QueryAttributeBool( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool match_value); + common::PlatformResult QueryAttributeInt( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + contacts_match_int_flag_e match, int match_value); + common::PlatformResult QueryAttributeString( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + contacts_match_str_flag_e match, const char* match_value); + common::PlatformResult QueryAttributeDate( + const std::string& attr_name, + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + contacts_match_int_flag_e match, int match_value); + common::PlatformResult QueryAttributeRangeBool( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool initial_value_is_set, bool initial_value, bool end_value_is_set, + bool end_value); + common::PlatformResult QueryAttributeRangeInt( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool initial_value_is_set, int initial_value, bool end_value_is_set, + int end_value); + common::PlatformResult QueryAttributeRangeString( + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + const char* initial_value, const char* end_value); + common::PlatformResult QueryAttributeRangeDate( + const std::string& attrName, + const FilterPropertyStruct& attribute_properties, LongSetPtr result, + bool initial_value_is_set, int initial_value, bool end_value_is_set, + int end_value); + common::PlatformResult SortContacts( + const FilterPropertyStruct& attribute_properties, LongVectorPtr result, + bool is_ascending, LongSetPtr ids); + common::PlatformResult SortContacts( + const FilterPropertyStruct& attribute_properties, LongVectorPtr result, + bool is_ascending); + + void GetIntersection(LongSetPtrVectorPtr id_sets, LongSetPtr result); + void GetUnion(LongSetPtrVectorPtr id_sets, LongSetPtr result); + + + static PropertiesMap s_properties_map_; + + long addressbook_id_; + bool is_addressbook_id_is_set_; + bool is_filter_set_; + bool is_sort_mode_set_; + + std::stack contact_id_set_array_stack_; + LongSetPtr filtered_contact_ids_; + LongVectorPtr sorted_contact_ids_; + + std::string sort_mode_attribute_; + bool is_sort_mode_asc_; +}; + +} // namespace contact +} // namespace extension + +#endif // CONTACT_CONTACT_SEARCH_ENGINE_H_ diff --git a/src/contact/contact_util.cc b/src/contact/contact_util.cc index 3bbcd81b..91038479 100755 --- a/src/contact/contact_util.cc +++ b/src/contact/contact_util.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,17 @@ namespace { static const std::string kSchema("file://"); +PlatformResult VerifyLocalPath(const std::string& path) { + // path should be either empty or point to existing local path + bool result = path.length() == 0 + || (path.length() > 0 && path[0] == '/' + && (access(path.c_str(), F_OK) == 0)); + return PlatformResult( + result ? ErrorCode::NO_ERROR : ErrorCode::INVALID_VALUES_ERR); +} + +} // namespace + std::string ConvertUriToPath(const std::string& str) { if (str.substr(0, kSchema.size()) == kSchema) { return str.substr(kSchema.size()); @@ -53,16 +64,7 @@ std::string ConvertPathToUri(const std::string& str) { return kSchema + str; } -PlatformResult VerifyLocalPath(const std::string& path) { - // path should be either empty or point to existing local path - bool result = path.length() == 0 - || (path.length() > 0 && path[0] == '/' - && (access(path.c_str(), F_OK) == 0)); - return PlatformResult( - result ? ErrorCode::NO_ERROR : ErrorCode::INVALID_VALUES_ERR); -} -} // namespace void ContactsDeleter(contacts_record_h* contacts_record) { if (CONTACTS_ERROR_NONE != contacts_record_destroy(*contacts_record, true)) { @@ -88,6 +90,8 @@ void ContactsQueryDeleter(contacts_query_h* contacts_query) { } } +using namespace common; + namespace { static const char kContactPhoneTypeHome[] = "HOME"; static const char kContactPhoneTypeWork[] = "WORK"; diff --git a/src/contact/contact_util.h b/src/contact/contact_util.h index 2652d1a9..ed806575 100755 --- a/src/contact/contact_util.h +++ b/src/contact/contact_util.h @@ -35,6 +35,9 @@ typedef std::string JsonString; namespace ContactUtil { +std::string ConvertUriToPath(const std::string& str); +std::string ConvertPathToUri(const std::string& str); + void ContactsDeleter(contacts_record_h *contacts_record); typedef std::unique_ptr ContactsRecordHPtr; diff --git a/src/contact/js/address_book.js b/src/contact/js/address_book.js index 52de1217..6cd5d578 100755 --- a/src/contact/js/address_book.js +++ b/src/contact/js/address_book.js @@ -509,23 +509,12 @@ AddressBook.prototype.find = function(successCallback, errorCallback, filter, so } catch (e) {} }); - //TODO: Move filtering to native code - try { - _contacts = C.filter(_contacts, args.filter); - } catch (e) { - native_.callIfPossible(errorCallback, e); - return; - } - - //TODO: Move sorting to native code - _contacts = C.sort(_contacts, args.sortMode); - native_.callIfPossible(successCallback, _contacts); }; native_.call('AddressBook_find', { - addressBook: this, - filter: filter, + addressBookId: this.id, + filter: utils_.repackFilter(filter), sortMode: sortMode }, callback); };