/* * Copyright (C) 2018 The Android Open Source Project * * 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 "utils/calendar/calendar-javaicu.h" #include "annotator/types.h" #include "utils/base/statusor.h" #include "utils/java/jni-base.h" #include "utils/java/jni-helper.h" namespace libtextclassifier3 { namespace { // Generic version of icu::Calendar::add with error checking. bool CalendarAdd(JniCache* jni_cache, JNIEnv* jenv, jobject calendar, jint field, jint value) { return JniHelper::CallVoidMethod(jenv, calendar, jni_cache->calendar_add, field, value) .ok(); } // Generic version of icu::Calendar::get with error checking. bool CalendarGet(JniCache* jni_cache, JNIEnv* jenv, jobject calendar, jint field, jint* value) { TC3_ASSIGN_OR_RETURN_FALSE( *value, JniHelper::CallIntMethod(jenv, calendar, jni_cache->calendar_get, field)); return true; } // Generic version of icu::Calendar::set with error checking. bool CalendarSet(JniCache* jni_cache, JNIEnv* jenv, jobject calendar, jint field, jint value) { return JniHelper::CallVoidMethod(jenv, calendar, jni_cache->calendar_set, field, value) .ok(); } // Extracts the first tag from a BCP47 tag (e.g. "en" for "en-US"). std::string GetFirstBcp47Tag(const std::string& tag) { for (size_t i = 0; i < tag.size(); ++i) { if (tag[i] == '_' || tag[i] == '-') { return std::string(tag, 0, i); } } return tag; } } // anonymous namespace Calendar::Calendar(JniCache* jni_cache) : jni_cache_(jni_cache), jenv_(jni_cache_ ? jni_cache->GetEnv() : nullptr), calendar_(nullptr, jenv_) {} bool Calendar::Initialize(const std::string& time_zone, const std::string& locale, int64 time_ms_utc) { if (!jni_cache_ || !jenv_) { TC3_LOG(ERROR) << "Initialize without env"; return false; } // We'll assume the day indices match later on, so verify it here. if (jni_cache_->calendar_sunday != kSunday || jni_cache_->calendar_monday != kMonday || jni_cache_->calendar_tuesday != kTuesday || jni_cache_->calendar_wednesday != kWednesday || jni_cache_->calendar_thursday != kThursday || jni_cache_->calendar_friday != kFriday || jni_cache_->calendar_saturday != kSaturday) { TC3_LOG(ERROR) << "day of the week indices mismatch"; return false; } // Get the time zone. TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef java_time_zone_str, JniHelper::NewStringUTF(jenv_, time_zone.c_str())); TC3_ASSIGN_OR_RETURN_FALSE( ScopedLocalRef java_time_zone, JniHelper::CallStaticObjectMethod(jenv_, jni_cache_->timezone_class.get(), jni_cache_->timezone_get_timezone, java_time_zone_str.get())); if (java_time_zone == nullptr) { TC3_LOG(ERROR) << "failed to get timezone"; return false; } // Get the locale. ScopedLocalRef java_locale(nullptr, jenv_); if (jni_cache_->locale_for_language_tag) { // API level 21+, we can actually parse language tags. TC3_ASSIGN_OR_RETURN_FALSE(ScopedLocalRef java_locale_str, JniHelper::NewStringUTF(jenv_, locale.c_str())); TC3_ASSIGN_OR_RETURN_FALSE( java_locale, JniHelper::CallStaticObjectMethod(jenv_, jni_cache_->locale_class.get(), jni_cache_->locale_for_language_tag, java_locale_str.get())); } else { // API level <21. We can't parse tags, so we just use the language. TC3_ASSIGN_OR_RETURN_FALSE( ScopedLocalRef java_language_str, JniHelper::NewStringUTF(jenv_, GetFirstBcp47Tag(locale).c_str())); TC3_ASSIGN_OR_RETURN_FALSE( java_locale, JniHelper::NewObject(jenv_, jni_cache_->locale_class.get(), jni_cache_->locale_init_string, java_language_str.get())); } if (java_locale == nullptr) { TC3_LOG(ERROR) << "failed to get locale"; return false; } // Get the calendar. TC3_ASSIGN_OR_RETURN_FALSE( calendar_, JniHelper::CallStaticObjectMethod( jenv_, jni_cache_->calendar_class.get(), jni_cache_->calendar_get_instance, java_time_zone.get(), java_locale.get())); if (calendar_ == nullptr) { TC3_LOG(ERROR) << "failed to get calendar"; return false; } // Set the time. if (!JniHelper::CallVoidMethod(jenv_, calendar_.get(), jni_cache_->calendar_set_time_in_millis, time_ms_utc) .ok()) { TC3_LOG(ERROR) << "failed to set time"; return false; } return true; } bool Calendar::GetFirstDayOfWeek(int* value) const { if (!jni_cache_ || !jenv_ || !calendar_) return false; TC3_ASSIGN_OR_RETURN_FALSE( *value, JniHelper::CallIntMethod(jenv_, calendar_.get(), jni_cache_->calendar_get_first_day_of_week)); return true; } bool Calendar::GetTimeInMillis(int64* value) const { if (!jni_cache_ || !jenv_ || !calendar_) return false; TC3_ASSIGN_OR_RETURN_FALSE( *value, JniHelper::CallLongMethod(jenv_, calendar_.get(), jni_cache_->calendar_get_time_in_millis)); return true; } CalendarLib::CalendarLib() { TC3_LOG(FATAL) << "Java ICU CalendarLib must be initialized with a JniCache."; } CalendarLib::CalendarLib(const std::shared_ptr& jni_cache) : jni_cache_(jni_cache) {} // Below is the boilerplate code for implementing the specialisations of // get/set/add for the various field types. #define TC3_DEFINE_FIELD_ACCESSOR(NAME, FIELD, KIND, TYPE) \ bool Calendar::KIND##NAME(TYPE value) const { \ if (!jni_cache_ || !jenv_ || !calendar_) return false; \ return Calendar##KIND(jni_cache_, jenv_, calendar_.get(), \ jni_cache_->calendar_##FIELD, value); \ } #define TC3_DEFINE_ADD(NAME, CONST) \ TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Add, int) #define TC3_DEFINE_SET(NAME, CONST) \ TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Set, int) #define TC3_DEFINE_GET(NAME, CONST) \ TC3_DEFINE_FIELD_ACCESSOR(NAME, CONST, Get, int*) TC3_DEFINE_ADD(Second, second) TC3_DEFINE_ADD(Minute, minute) TC3_DEFINE_ADD(HourOfDay, hour_of_day) TC3_DEFINE_ADD(DayOfMonth, day_of_month) TC3_DEFINE_ADD(Year, year) TC3_DEFINE_ADD(Month, month) TC3_DEFINE_GET(DayOfWeek, day_of_week) TC3_DEFINE_SET(ZoneOffset, zone_offset) TC3_DEFINE_SET(DstOffset, dst_offset) TC3_DEFINE_SET(Year, year) TC3_DEFINE_SET(Month, month) TC3_DEFINE_SET(DayOfYear, day_of_year) TC3_DEFINE_SET(DayOfMonth, day_of_month) TC3_DEFINE_SET(DayOfWeek, day_of_week) TC3_DEFINE_SET(HourOfDay, hour_of_day) TC3_DEFINE_SET(Minute, minute) TC3_DEFINE_SET(Second, second) TC3_DEFINE_SET(Millisecond, millisecond) #undef TC3_DEFINE_FIELD_ACCESSOR #undef TC3_DEFINE_ADD #undef TC3_DEFINE_SET #undef TC3_DEFINE_GET } // namespace libtextclassifier3